From 29b2349697db580f1b32e860c93bee31e8837397 Mon Sep 17 00:00:00 2001 From: "Ruokun (Tommy) Niu" Date: Fri, 23 Aug 2024 09:55:16 -0700 Subject: [PATCH] Removed .unwrap() in VariableValue + resolved warnings (#21) * fixed expression/mod.rs * updated aggregating functions * init work on expression/mod.rs * revised aggregating functions * Updated all functions with function error * updated unknownproperty variant * swapped unwrap() in scalar functions * revised error enum based on comments * removed invalidclock * revised format error * changes in core * reverted irrelevant files * reverted irrelevant files * resolved warnings * updated the error message for invalidFormat errors * created constants for format error messages * nit * running cargo fmt --- core/src/evaluation/expressions/mod.rs | 682 +++++--- .../expressions/tests/list_functions.rs | 39 +- .../expressions/tests/list_indexing.rs | 678 ++++---- .../evaluation/functions/aggregation/avg.rs | 131 +- .../evaluation/functions/aggregation/count.rs | 258 +-- .../evaluation/functions/aggregation/last.rs | 73 +- .../functions/aggregation/linear_gradient.rs | 111 +- .../evaluation/functions/aggregation/max.rs | 383 ++++- .../evaluation/functions/aggregation/min.rs | 377 +++- .../evaluation/functions/aggregation/sum.rs | 213 ++- .../evaluation/functions/context_mutators.rs | 4 +- .../functions/cypher_scalar/char_length.rs | 16 +- .../functions/cypher_scalar/coalesce.rs | 4 +- .../functions/cypher_scalar/head.rs | 16 +- .../functions/cypher_scalar/last.rs | 16 +- .../functions/cypher_scalar/size.rs | 16 +- .../cypher_scalar/tests/head_tests.rs | 9 +- .../cypher_scalar/tests/last_tests.rs | 9 +- .../functions/cypher_scalar/timestamp.rs | 35 +- .../functions/cypher_scalar/to_boolean.rs | 44 +- .../functions/cypher_scalar/to_float.rs | 54 +- .../functions/cypher_scalar/to_integer.rs | 46 +- core/src/evaluation/functions/drasi/max.rs | 18 +- core/src/evaluation/functions/drasi/min.rs | 18 +- core/src/evaluation/functions/drasi/stdevp.rs | 38 +- .../evaluation/functions/future/awaiting.rs | 54 +- .../functions/future/future_element.rs | 60 +- .../evaluation/functions/future/true_for.rs | 437 +++-- .../evaluation/functions/future/true_later.rs | 46 +- .../evaluation/functions/future/true_until.rs | 74 +- core/src/evaluation/functions/list/range.rs | 87 +- core/src/evaluation/functions/list/reduce.rs | 48 +- core/src/evaluation/functions/list/tail.rs | 16 +- core/src/evaluation/functions/metadata.rs | 154 +- core/src/evaluation/functions/mod.rs | 14 +- core/src/evaluation/functions/numeric/abs.rs | 96 +- core/src/evaluation/functions/numeric/ceil.rs | 58 +- .../src/evaluation/functions/numeric/floor.rs | 53 +- .../functions/numeric/numeric_round.rs | 644 ++++++- .../evaluation/functions/numeric/random.rs | 65 +- core/src/evaluation/functions/numeric/sign.rs | 116 +- .../functions/numeric/tests/abs_tests.rs | 408 ++--- .../functions/numeric/tests/ceil_tests.rs | 368 ++-- .../functions/numeric/tests/floor_tests.rs | 408 ++--- .../functions/numeric/tests/random_tests.rs | 145 +- .../functions/numeric/tests/round_tests.rs | 1266 +++++++------- .../functions/numeric/tests/sign_tests.rs | 340 ++-- core/src/evaluation/functions/past/mod.rs | 120 +- .../temporal_duration/temporal_duration.rs | 553 ++++-- .../temporal_instant/temporal_instant.rs | 1516 +++++++++++++---- .../functions/text/tests/left_tests.rs | 44 +- .../functions/text/tests/replace_tests.rs | 22 +- .../functions/text/tests/reverse_tests.rs | 17 +- .../functions/text/tests/right_tests.rs | 40 +- .../functions/text/tests/split_tests.rs | 30 +- .../functions/text/tests/substring_tests.rs | 63 +- .../functions/text/tests/to_lower_tests.rs | 22 +- .../text/tests/to_string_or_null_tests.rs | 244 +-- .../functions/text/tests/to_string_tests.rs | 14 +- .../functions/text/tests/to_upper_tests.rs | 22 +- .../functions/text/tests/trim_tests.rs | 58 +- core/src/evaluation/functions/text/text.rs | 338 +++- core/src/evaluation/mod.rs | 116 +- core/src/evaluation/temporal_constants.rs | 37 +- core/src/interface/mod.rs | 15 + 65 files changed, 7678 insertions(+), 3838 deletions(-) diff --git a/core/src/evaluation/expressions/mod.rs b/core/src/evaluation/expressions/mod.rs index 00696dd..d996d35 100644 --- a/core/src/evaluation/expressions/mod.rs +++ b/core/src/evaluation/expressions/mod.rs @@ -6,11 +6,13 @@ use std::sync::Arc; use async_recursion::async_recursion; use chrono::{ - Datelike, Duration, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Weekday, + Datelike, Duration, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Weekday, }; use drasi_query_ast::ast; -use crate::evaluation::temporal_constants; +use crate::evaluation::temporal_constants::{ + self, EPOCH_MIDNIGHT_NAIVE_DATETIME, MIDNIGHT_NAIVE_TIME, UTC_FIXED_OFFSET, +}; use crate::evaluation::variable_value::duration::Duration as DurationStruct; use crate::evaluation::variable_value::float::Float; use crate::evaluation::variable_value::integer::Integer; @@ -23,7 +25,7 @@ use crate::{evaluation::variable_value::VariableValue, interface::ResultIndex}; use super::{ context::{ExpressionEvaluationContext, SideEffects}, functions::{aggregation::Accumulator, Function, FunctionRegistry}, - EvaluationError, + EvaluationError, FunctionEvaluationError, }; pub struct ExpressionEvaluator { @@ -134,33 +136,37 @@ impl ExpressionEvaluator { ast::Literal::Null => VariableValue::Null, ast::Literal::Date(_) => VariableValue::Date(*temporal_constants::EPOCH_NAIVE_DATE), ast::Literal::Integer(i) => VariableValue::Integer(Integer::from( - serde_json::Number::from(*i).as_i64().unwrap(), - )), //NOte might need to consider both integer and float situations + match serde_json::Number::from(*i).as_i64() { + Some(n) => n, + None => return Err(EvaluationError::ParseError), + }, + )), ast::Literal::Real(r) => match serde_json::Number::from_f64(*r) { - Some(n) => VariableValue::Float(Float::from(n.as_f64().unwrap())), + Some(n) => VariableValue::Float(Float::from(match n.as_f64() { + Some(n) => n, + None => return Err(EvaluationError::ParseError), + })), None => VariableValue::Null, }, - ast::Literal::LocalTime(_t) => { - VariableValue::LocalTime(NaiveTime::from_hms_opt(0, 0, 0).unwrap()) - } + ast::Literal::LocalTime(_t) => VariableValue::LocalTime(*MIDNIGHT_NAIVE_TIME), ast::Literal::ZonedTime(_t) => VariableValue::ZonedTime(ZonedTime::new( - NaiveTime::from_hms_opt(0, 0, 0).unwrap(), - FixedOffset::east_opt(0).unwrap(), + *MIDNIGHT_NAIVE_TIME, + *UTC_FIXED_OFFSET, )), - ast::Literal::LocalDateTime(_dt) => VariableValue::LocalDateTime( - NaiveDate::from_ymd_opt(1970, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap(), - ), + ast::Literal::LocalDateTime(_dt) => { + VariableValue::LocalDateTime(*EPOCH_MIDNIGHT_NAIVE_DATETIME) + } ast::Literal::ZonedDateTime(_dt) => { + let local_datetime = *EPOCH_MIDNIGHT_NAIVE_DATETIME; VariableValue::ZonedDateTime(ZonedDateTime::new( - NaiveDate::from_ymd_opt(1970, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap() - .and_local_timezone(FixedOffset::east_opt(0).unwrap()) - .unwrap(), + match local_datetime.and_local_timezone(*UTC_FIXED_OFFSET) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "DateTime".to_string(), + }) + } + }, None, )) } @@ -172,12 +178,18 @@ impl ExpressionEvaluator { for (key, value) in o { let val = match value { ast::Literal::Integer(i) => VariableValue::Integer(Integer::from( - serde_json::Number::from(*i).as_i64().unwrap(), + match serde_json::Number::from(*i).as_i64() { + Some(n) => n, + None => return Err(EvaluationError::ParseError), + }, )), ast::Literal::Text(s) => VariableValue::String(s.to_string()), ast::Literal::Real(r) => match serde_json::Number::from_f64(*r) { - Some(n) => VariableValue::Float(Float::from(n.as_f64().unwrap())), - None => VariableValue::Null, + Some(n) => VariableValue::Float(Float::from(match n.as_f64() { + Some(n) => n, + None => return Err(EvaluationError::OverflowError), + })), + None => return Err(EvaluationError::ParseError), }, _ => VariableValue::Null, }; @@ -200,21 +212,23 @@ impl ExpressionEvaluator { Some(v) => v.clone(), None => VariableValue::Null, }, - VariableValue::Date(d) => match get_date_property(*d, (*key).to_string()) - .await - { - Some(v) => VariableValue::Integer(v.into()), - None => { - return Err(EvaluationError::UnknownFunction((*key).to_string())) + VariableValue::Date(d) => { + match get_date_property(*d, (*key).to_string()).await { + Some(v) => VariableValue::Integer(v.into()), + None => { + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) + } } - }, + } VariableValue::LocalTime(t) => { match get_local_time_property(*t, (*key).to_string()).await { Some(v) => VariableValue::Integer(v.into()), None => { - return Err(EvaluationError::UnknownFunction( - (*key).to_string(), - )) + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) } } } @@ -237,9 +251,9 @@ impl ExpressionEvaluator { } } None => { - return Err(EvaluationError::UnknownFunction( - (*key).to_string(), - )) + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) } } } @@ -247,9 +261,9 @@ impl ExpressionEvaluator { match get_local_datetime_property(*t, (*key).to_string()).await { Some(v) => VariableValue::Integer(v.into()), None => { - return Err(EvaluationError::UnknownFunction( - (*key).to_string(), - )) + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) } } } @@ -272,9 +286,9 @@ impl ExpressionEvaluator { } } None => { - return Err(EvaluationError::UnknownFunction( - (*key).to_string(), - )) + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) } } } @@ -282,9 +296,9 @@ impl ExpressionEvaluator { match get_duration_property(d.clone(), (*key).to_string()).await { Some(v) => VariableValue::Integer(v.into()), None => { - return Err(EvaluationError::UnknownFunction( - (*key).to_string(), - )) + return Err(EvaluationError::UnknownProperty { + property_name: (*key).to_string(), + }) } } } @@ -402,35 +416,41 @@ impl ExpressionEvaluator { (Some(start), Some(end)) => { let start = self.evaluate_expression(context, start).await?; if !start.is_i64() { - return Err(EvaluationError::InvalidType); + return Err(EvaluationError::OverflowError); } let end = self.evaluate_expression(context, end).await?; if !end.is_i64() { - return Err(EvaluationError::InvalidType); + return Err(EvaluationError::OverflowError); } VariableValue::ListRange(ListRange { - start: RangeBound::Index(start.as_i64().unwrap()), - end: RangeBound::Index(end.as_i64().unwrap()), + start: RangeBound::Index(match start.as_i64() { + Some(n) => n, + None => return Err(EvaluationError::OverflowError), + }), + end: RangeBound::Index(match end.as_i64() { + Some(n) => n, + None => return Err(EvaluationError::OverflowError), + }), }) } (Some(start), None) => { let start = self.evaluate_expression(context, start).await?; - if !start.is_i64() { - return Err(EvaluationError::InvalidType); - } VariableValue::ListRange(ListRange { - start: RangeBound::Index(start.as_i64().unwrap()), + start: RangeBound::Index(match start.as_i64() { + Some(n) => n, + None => return Err(EvaluationError::OverflowError), + }), end: RangeBound::Unbounded, }) } (None, Some(end)) => { let end = self.evaluate_expression(context, end).await?; - if !end.is_i64() { - return Err(EvaluationError::InvalidType); - } VariableValue::ListRange(ListRange { start: RangeBound::Unbounded, - end: RangeBound::Index(end.as_i64().unwrap()), + end: RangeBound::Index(match end.as_i64() { + Some(n) => n, + None => return Err(EvaluationError::OverflowError), + }), }) } (None, None) => VariableValue::ListRange(ListRange { @@ -507,12 +527,26 @@ impl ExpressionEvaluator { (VariableValue::Float(n1), VariableValue::Float(n2)) => { VariableValue::Bool(n1 != n2) } - (VariableValue::Float(n1), VariableValue::Integer(n2)) => { - VariableValue::Bool(n1 != n2.as_i64().unwrap() as f64) - } - (VariableValue::Integer(n1), VariableValue::Float(n2)) => { - VariableValue::Bool(n2 != n1.as_i64().unwrap() as f64) - } + (VariableValue::Float(n1), VariableValue::Integer(n2)) => VariableValue::Bool( + n1 != match n2.as_i64() { + Some(n) => n as f64, + None => { + return Err(EvaluationError::InvalidType { + expected: "Integer".to_string(), + }) + } + }, + ), + (VariableValue::Integer(n1), VariableValue::Float(n2)) => VariableValue::Bool( + n2 != match n1.as_i64() { + Some(n) => n as f64, + None => { + return Err(EvaluationError::InvalidType { + expected: "Integer".to_string(), + }) + } + }, + ), (VariableValue::String(s1), VariableValue::String(s2)) => { VariableValue::Bool(s1 != s2) } @@ -568,7 +602,7 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 < dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 < dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::LocalTime(time2)) => { @@ -580,25 +614,43 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 < dt2) } (VariableValue::Date(date1), VariableValue::LocalDateTime(dt2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 < dt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedTime(time2)) => { - let dt1 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()) - .and_local_timezone(*time1.offset()) - .unwrap(); - let dt2 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()) - .and_local_timezone(*time2.offset()) - .unwrap(); + let dt1 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time1.time(), + ) + .and_local_timezone(*time1.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + let dt2 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time2.time(), + ) + .and_local_timezone(*time2.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; VariableValue::Bool(dt1 < dt2) } (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedDateTime(zdt2)) => { VariableValue::Bool(zdt1.datetime() < zdt2.datetime()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(zdt1.datetime() < &dt2.and_utc()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::LocalDateTime(dt2)) => { @@ -611,22 +663,33 @@ impl ExpressionEvaluator { (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedTime(time2)) => { let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()); - VariableValue::Bool( - zdt1.datetime() < &dt2.and_local_timezone(*time2.offset()).unwrap(), - ) + let zdt2 = match dt2.and_local_timezone(*time2.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1.datetime() < &zdt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedDateTime(zdt2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - VariableValue::Bool( - dt1.and_local_timezone(zdt2.datetime().timezone()).unwrap() - < *zdt2.datetime(), - ) + let zdt1 = match dt1.and_local_timezone(*time1.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "DateTiie".to_string(), + }) + } + }; + VariableValue::Bool(zdt1 < *zdt2.datetime()) } (VariableValue::ZonedTime(time1), VariableValue::Date(date2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 < dt2) } (VariableValue::ZonedTime(time1), VariableValue::LocalDateTime(dt2)) => { @@ -682,7 +745,7 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 <= dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 <= dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::LocalTime(time2)) => { @@ -694,22 +757,40 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 <= dt2) } (VariableValue::Date(date1), VariableValue::LocalDateTime(dt2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 <= dt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedTime(time2)) => { - let dt1 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()) - .and_local_timezone(*time1.offset()) - .unwrap(); - let dt2 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()) - .and_local_timezone(*time2.offset()) - .unwrap(); + let dt1 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time1.time(), + ) + .and_local_timezone(*time1.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + let dt2 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time2.time(), + ) + .and_local_timezone(*time2.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; VariableValue::Bool(dt1 <= dt2) } (VariableValue::ZonedDateTime(zdt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(zdt1.datetime() <= &dt2.and_utc()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::LocalDateTime(dt2)) => { @@ -722,22 +803,33 @@ impl ExpressionEvaluator { (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedTime(time2)) => { let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()); - VariableValue::Bool( - zdt1.datetime() <= &dt2.and_local_timezone(*time2.offset()).unwrap(), - ) + let zdt2 = match dt2.and_local_timezone(*time2.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1.datetime() <= &zdt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedDateTime(zdt2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - VariableValue::Bool( - dt1.and_local_timezone(zdt2.datetime().timezone()).unwrap() - <= *zdt2.datetime(), - ) + let zdt1 = match dt1.and_local_timezone(*time1.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "DateTime".to_string(), + }) + } + }; + VariableValue::Bool(zdt1 <= *zdt2.datetime()) } (VariableValue::ZonedTime(time1), VariableValue::Date(date2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 <= dt2) } (VariableValue::ZonedTime(time1), VariableValue::LocalDateTime(dt2)) => { @@ -790,7 +882,7 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 > dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 > dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::LocalTime(time2)) => { @@ -802,25 +894,44 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 > dt2) } (VariableValue::Date(date1), VariableValue::LocalDateTime(dt2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 > dt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedTime(time2)) => { - let dt1 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()) - .and_local_timezone(*time1.offset()) - .unwrap(); - let dt2 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()) - .and_local_timezone(*time2.offset()) - .unwrap(); + let dt1 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time1.time(), + ) + .and_local_timezone(*time1.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + let dt2 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time2.time(), + ) + .and_local_timezone(*time2.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(dt1 > dt2) } (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedDateTime(zdt2)) => { VariableValue::Bool(zdt1.datetime() > zdt2.datetime()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(zdt1.datetime() > &dt2.and_utc()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::LocalDateTime(dt2)) => { @@ -833,22 +944,33 @@ impl ExpressionEvaluator { (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedTime(time2)) => { let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()); - VariableValue::Bool( - zdt1.datetime() > &dt2.and_local_timezone(*time2.offset()).unwrap(), - ) + let zdt2 = match dt2.and_local_timezone(*time2.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1.datetime() > &zdt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedDateTime(zdt2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - VariableValue::Bool( - dt1.and_local_timezone(zdt2.datetime().timezone()).unwrap() - > *zdt2.datetime(), - ) + let zdt1 = match dt1.and_local_timezone(*time1.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1 > *zdt2.datetime()) } (VariableValue::ZonedTime(time1), VariableValue::Date(date2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 > dt2) } (VariableValue::ZonedTime(time1), VariableValue::LocalDateTime(dt2)) => { @@ -857,21 +979,27 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 > dt2) } (VariableValue::Date(date1), VariableValue::ZonedDateTime(zdt2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(&dt1.and_utc() > zdt2.datetime()) } (VariableValue::Date(date1), VariableValue::LocalTime(time2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, time2); VariableValue::Bool(dt1 > dt2) } (VariableValue::Date(date1), VariableValue::ZonedTime(time2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()); - VariableValue::Bool( - &dt1.and_utc() > &dt2.and_local_timezone(*time2.offset()).unwrap(), - ) + let zdt2 = match dt2.and_local_timezone(*time2.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(dt1.and_utc() > zdt2) } (VariableValue::ZonedTime(time1), VariableValue::LocalTime(time2)) => { let dt1 = @@ -918,7 +1046,7 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 >= dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 >= dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::LocalTime(time2)) => { @@ -930,14 +1058,14 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 >= dt2) } (VariableValue::Date(date1), VariableValue::LocalDateTime(dt2)) => { - let dt1 = NaiveDateTime::new(date1, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt1 = NaiveDateTime::new(date1, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 >= dt2) } (VariableValue::LocalDateTime(dt1), VariableValue::ZonedDateTime(zdt2)) => { VariableValue::Bool(dt1 >= zdt2.datetime().naive_utc()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::Date(date2)) => { - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(zdt1.datetime() >= &dt2.and_utc()) } (VariableValue::ZonedDateTime(zdt1), VariableValue::LocalDateTime(dt2)) => { @@ -950,22 +1078,33 @@ impl ExpressionEvaluator { (VariableValue::ZonedDateTime(zdt1), VariableValue::ZonedTime(time2)) => { let dt2 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()); - VariableValue::Bool( - zdt1.datetime() >= &dt2.and_local_timezone(*time2.offset()).unwrap(), - ) + let zdt2 = match dt2.and_local_timezone(*time2.offset()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1.datetime() >= &zdt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedDateTime(zdt2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - VariableValue::Bool( - dt1.and_local_timezone(zdt2.datetime().timezone()).unwrap() - >= *zdt2.datetime(), - ) + let zdt1 = match dt1.and_local_timezone(zdt2.datetime().timezone()) { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + VariableValue::Bool(zdt1 >= *zdt2.datetime()) } (VariableValue::ZonedTime(time1), VariableValue::Date(date2)) => { let dt1 = NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()); - let dt2 = NaiveDateTime::new(date2, NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + let dt2 = NaiveDateTime::new(date2, *MIDNIGHT_NAIVE_TIME); VariableValue::Bool(dt1 >= dt2) } (VariableValue::ZonedTime(time1), VariableValue::LocalDateTime(dt2)) => { @@ -980,14 +1119,32 @@ impl ExpressionEvaluator { VariableValue::Bool(dt1 >= dt2) } (VariableValue::ZonedTime(time1), VariableValue::ZonedTime(time2)) => { - let dt1 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time1.time()) - .and_local_timezone(*time1.offset()) - .unwrap(); - let dt2 = - NaiveDateTime::new(*temporal_constants::EPOCH_NAIVE_DATE, *time2.time()) - .and_local_timezone(*time2.offset()) - .unwrap(); + let dt1 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time1.time(), + ) + .and_local_timezone(*time1.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; + let dt2 = match NaiveDateTime::new( + *temporal_constants::EPOCH_NAIVE_DATE, + *time2.time(), + ) + .and_local_timezone(*time2.offset()) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(EvaluationError::InvalidType { + expected: "Time".to_string(), + }) + } + }; VariableValue::Bool(dt1 >= dt2) } (VariableValue::Duration(d1), VariableValue::Duration(d2)) => { @@ -1176,37 +1333,37 @@ impl ExpressionEvaluator { let n2 = self.evaluate_expression(context, e2).await?; match (n1, n2) { (VariableValue::Integer(n1), VariableValue::Integer(n2)) => { - let m1 = n1.as_i64().ok_or(EvaluationError::InvalidType)?; - let m2 = n2.as_i64().ok_or(EvaluationError::InvalidType)?; + let m1 = n1.as_i64().ok_or(EvaluationError::OverflowError)?; + let m2 = n2.as_i64().ok_or(EvaluationError::OverflowError)?; if m2 == 0 { - VariableValue::Null + return Err(EvaluationError::DivideByZero); } else { VariableValue::Float(Float::from(m1 as f64 / m2 as f64)) } } (VariableValue::Float(n1), VariableValue::Float(n2)) => { - let m1 = n1.as_f64().ok_or(EvaluationError::InvalidType)?; - let m2 = n2.as_f64().ok_or(EvaluationError::InvalidType)?; + let m1 = n1.as_f64().ok_or(EvaluationError::OverflowError)?; + let m2 = n2.as_f64().ok_or(EvaluationError::OverflowError)?; if m2 == 0.0 { - VariableValue::Null + return Err(EvaluationError::DivideByZero); } else { VariableValue::Float(Float::from(m1 / m2)) } } (VariableValue::Float(n1), VariableValue::Integer(n2)) => { - let m1 = n1.as_f64().ok_or(EvaluationError::InvalidType)?; - let m2 = n2.as_i64().ok_or(EvaluationError::InvalidType)?; + let m1 = n1.as_f64().ok_or(EvaluationError::OverflowError)?; + let m2 = n2.as_i64().ok_or(EvaluationError::OverflowError)?; if m2 == 0 { - VariableValue::Null + return Err(EvaluationError::DivideByZero); } else { VariableValue::Float(Float::from(m1 / m2 as f64)) } } (VariableValue::Integer(n1), VariableValue::Float(n2)) => { - let m1 = n1.as_i64().ok_or(EvaluationError::InvalidType)?; - let m2 = n2.as_f64().ok_or(EvaluationError::InvalidType)?; + let m1 = n1.as_i64().ok_or(EvaluationError::OverflowError)?; + let m2 = n2.as_f64().ok_or(EvaluationError::OverflowError)?; if m2 == 0.0 { - VariableValue::Null + return Err(EvaluationError::DivideByZero); } else { VariableValue::Float(Float::from(m1 as f64 / m2)) } @@ -1218,7 +1375,11 @@ impl ExpressionEvaluator { let e1 = self.evaluate_expression(context, e1).await?; match self.evaluate_expression(context, e2).await? { VariableValue::List(a) => VariableValue::Bool(a.contains(&e1)), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(EvaluationError::InvalidType { + expected: "List".to_string(), + }) + } } } ast::BinaryExpression::Modulo(e1, e2) => { @@ -1260,7 +1421,7 @@ impl ExpressionEvaluator { n2.as_i64() .unwrap_or_default() .try_into() - .map_err(|_| EvaluationError::InvalidType)?, + .map_err(|_| EvaluationError::OverflowError)?, ), )) } @@ -1291,7 +1452,11 @@ impl ExpressionEvaluator { let subject = self.evaluate_expression(context, e1).await?; let label = match self.evaluate_expression(context, e2).await? { VariableValue::String(s) => Arc::from(s), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(EvaluationError::InvalidType { + expected: "String".to_string(), + }) + } }; match subject { @@ -1304,7 +1469,14 @@ impl ExpressionEvaluator { ast::BinaryExpression::Index(e1, e2) => { let index_exp = self.evaluate_expression(context, e2).await?; let variable_value_list = self.evaluate_expression(context, e1).await?; - let list = variable_value_list.as_array().unwrap(); + let list = match variable_value_list.as_array() { + Some(list) => list, + None => { + return Err(EvaluationError::InvalidType { + expected: "List".to_string(), + }) + } + }; match index_exp { VariableValue::ListRange(list_range) => { let start_bound = match list_range.start { @@ -1335,10 +1507,14 @@ impl ExpressionEvaluator { return Ok(VariableValue::List(result)); } VariableValue::Integer(index) => { - if !index.is_i64() { - return Err(EvaluationError::InvalidType); - } - let index_i64 = index.as_i64().unwrap(); + let index_i64 = match index.as_i64() { + Some(index) => index, + None => { + return Err(EvaluationError::InvalidType { + expected: "Integer".to_string(), + }) + } + }; if index_i64 >= list.len() as i64 { return Ok(VariableValue::Null); } @@ -1347,11 +1523,23 @@ impl ExpressionEvaluator { let element = list[index_i64 as usize].clone(); return Ok(element); } - let element = list[index.as_i64().unwrap() as usize].clone(); + let index = match index.as_i64() { + Some(index) => index as usize, + None => { + return Err(EvaluationError::InvalidType { + expected: "Integer".to_string(), + }) + } + }; + let element = list[index].clone(); return Ok(element); } - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(EvaluationError::InvalidType { + expected: "Integer or ListRange".to_string(), + }) + } } } }; @@ -1373,18 +1561,12 @@ impl ExpressionEvaluator { scalar .call(context, expression, values) .await - .map_err(|e| EvaluationError::FunctionError { - function_name: expression.name.to_string(), - error: Box::new(e), - })? + .map_err(|e| EvaluationError::FunctionError(e))? } Function::LazyScalar(scalar) => scalar .call(context, expression, &expression.args) .await - .map_err(|e| EvaluationError::FunctionError { - function_name: expression.name.to_string(), - error: Box::new(e), - })?, + .map_err(|e| EvaluationError::FunctionError(e))?, Function::Aggregating(aggregate) => { let mut values = Vec::new(); for arg in &expression.args { @@ -1431,24 +1613,15 @@ impl ExpressionEvaluator { SideEffects::Apply => aggregate .apply(context, values, &mut accumulator) .await - .map_err(|e| EvaluationError::FunctionError { - function_name: expression.name.to_string(), - error: Box::new(e), - })?, + .map_err(|e| EvaluationError::FunctionError(e))?, SideEffects::RevertForUpdate | SideEffects::RevertForDelete => aggregate .revert(context, values, &mut accumulator) .await - .map_err(|e| EvaluationError::FunctionError { - function_name: expression.name.to_string(), - error: Box::new(e), - })?, + .map_err(|e| EvaluationError::FunctionError(e))?, SideEffects::Snapshot => aggregate .snapshot(context, values, &accumulator) .await - .map_err(|e| EvaluationError::FunctionError { - function_name: expression.name.to_string(), - error: Box::new(e), - })?, + .map_err(|e| EvaluationError::FunctionError(e))?, }; //println!("{:?} {}{} : {:?}", context.get_side_effects(), expression.name, expression.position_in_query, result); @@ -1470,7 +1643,12 @@ impl ExpressionEvaluator { if expression.args.is_empty() { VariableValue::Null } else { - let new_context = context_mutator.call(context, expression).await?; + let new_context = match context_mutator.call(context, expression).await { + Ok(new_context) => new_context, + Err(e) => { + return Err(EvaluationError::FunctionError(e)); + } + }; self.evaluate_expression(&new_context, &expression.args[0]) .await? @@ -1553,23 +1731,42 @@ impl ExpressionEvaluator { &self, context: &ExpressionEvaluationContext<'_>, expression: &ast::Expression, - ) -> Result<(Arc, VariableValue), EvaluationError> { + ) -> Result<(Arc, VariableValue), FunctionEvaluationError> { match expression { ast::Expression::BinaryExpression(exp) => match exp { ast::BinaryExpression::Eq(var, val) => { let variable = match *var.clone() { ast::Expression::UnaryExpression(exp) => match exp { ast::UnaryExpression::Identifier(ident) => ident, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionEvaluationError::InvalidType { + expected: "Identifier expression".to_string(), + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionEvaluationError::InvalidType { + expected: "Unary expression".to_string(), + }) + } + }; + let value = match self.evaluate_expression(context, val).await { + Ok(value) => value, + Err(_) => { + return Err(FunctionEvaluationError::InvalidType { + expected: "valid identifier expresssion".to_string(), + }) + } }; - let value = self.evaluate_expression(context, val).await?; Ok((variable, value)) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionEvaluationError::InvalidType { + expected: "Eq expression".to_string(), + }), }, - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionEvaluationError::InvalidType { + expected: "Binary expression".to_string(), + }), } } @@ -1616,7 +1813,9 @@ impl ExpressionEvaluator { } Ok(VariableValue::List(result)) } - _ => Err(EvaluationError::InvalidType), + _ => Err(EvaluationError::InvalidType { + expected: "List".to_string(), + }), } } @@ -1667,14 +1866,16 @@ impl ExpressionEvaluator { } Ok(result) } - _ => Err(EvaluationError::InvalidType), + _ => Err(EvaluationError::InvalidType { + expected: "List".to_string(), + }), } } } async fn get_date_property(date: NaiveDate, property: String) -> Option { match property.as_str() { - "year" => Some(date.year().try_into().unwrap()), + "year" => Some(date.year() as u32), "month" => Some(date.month()), "day" => Some(date.day()), "quarter" => Some((date.month() - 1) / 3 + 1), @@ -1706,22 +1907,27 @@ async fn get_date_property(date: NaiveDate, property: String) -> Option { Some(day_of_week_num) } "ordinalDay" => Some(date.ordinal()), - "weekYear" => Some(date.iso_week().year().try_into().unwrap()), + "weekYear" => Some(date.iso_week().year() as u32), "dayOfQuarter" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; + let duration = date - start_date; let num_days = duration.num_days(); - Some((num_days + 1).try_into().unwrap()) + Some((num_days + 1) as u32) } "quarterDay" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; let duration = date - start_date; let num_days = duration.num_days(); - Some((num_days + 1).try_into().unwrap()) + Some((num_days + 1) as u32) } _ => None, } @@ -1767,7 +1973,7 @@ async fn get_local_datetime_property(datetime: NaiveDateTime, property: String) let date = datetime.date(); let time = datetime.time(); match property.as_str() { - "year" => Some(date.year().try_into().unwrap()), + "year" => Some(date.year() as u32), "month" => Some(date.month()), "day" => Some(date.day()), "quarter" => Some((date.month() - 1) / 3 + 1), @@ -1799,22 +2005,26 @@ async fn get_local_datetime_property(datetime: NaiveDateTime, property: String) Some(day_of_week_num) } "ordinalDay" => Some(date.ordinal()), - "weekYear" => Some(date.iso_week().year().try_into().unwrap()), + "weekYear" => Some(date.iso_week().year() as u32), "dayOfQuarter" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; let duration = date - start_date; let num_days = duration.num_days(); - Some((num_days + 1).try_into().unwrap()) + Some((num_days + 1) as u32) } "quarterDay" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; let duration = date - start_date; let num_days = duration.num_days(); - Some((num_days + 1).try_into().unwrap()) + Some((num_days + 1) as u32) } "hour" => Some(time.hour()), "minute" => Some(time.minute()), @@ -1870,16 +2080,20 @@ async fn get_datetime_property(zoned_datetime: ZonedDateTime, property: String) "weekYear" => Some(date.iso_week().year().to_string()), "dayOfQuarter" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; let duration = date - start_date; let num_days = duration.num_days(); Some((num_days + 1).to_string()) } "quarterDay" => { let quarter = (date.month() - 1) / 3 + 1; - let start_date = - NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1).unwrap(); + let start_date = match NaiveDate::from_ymd_opt(date.year(), (quarter - 1) * 3 + 1, 1) { + Some(date) => date, + None => return None, + }; let duration = date - start_date; let num_days = duration.num_days(); Some((num_days + 1).to_string()) @@ -1904,19 +2118,13 @@ async fn get_datetime_property(zoned_datetime: ZonedDateTime, property: String) Some(minutes.to_string()) } "epochMillis" => { - let epoch = NaiveDate::from_ymd_opt(1970, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap(); + let epoch = *EPOCH_MIDNIGHT_NAIVE_DATETIME; let duration = datetime.naive_utc() - epoch; let millis = duration.num_milliseconds(); Some(millis.to_string()) } "epochSeconds" => { - let epoch = NaiveDate::from_ymd_opt(1970, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap(); + let epoch = *EPOCH_MIDNIGHT_NAIVE_DATETIME; let duration = datetime.naive_utc() - epoch; let seconds = duration.num_seconds(); Some(seconds.to_string()) @@ -1940,8 +2148,14 @@ async fn get_duration_property(duration_struct: DurationStruct, property: String "minutes" => Some(duration.num_minutes()), "seconds" => Some(duration.num_seconds()), "milliseconds" => Some(duration.num_milliseconds()), - "microseconds" => Some(duration.num_microseconds().unwrap()), - "nanoseconds" => Some(duration.num_nanoseconds().unwrap()), + "microseconds" => Some(match duration.num_microseconds() { + Some(micros) => micros, + None => 0, + }), + "nanoseconds" => Some(match duration.num_nanoseconds() { + Some(nanos) => nanos, + None => 0, + }), "quartersOfYear" => { let quarters = month / 3 + 1; Some(quarters) @@ -1980,11 +2194,17 @@ async fn get_duration_property(duration_struct: DurationStruct, property: String Some(millis) } "microsecondsOfSecond" => { - let micros = duration.num_microseconds().unwrap() % 1000000; + let micros = match duration.num_microseconds() { + Some(micros) => micros, + None => 0, + } % 1000000; Some(micros) } "nanosecondsOfSecond" => { - let nanos = duration.num_nanoseconds().unwrap() % 1000000000; + let nanos = match duration.num_nanoseconds() { + Some(nanos) => nanos, + None => 0, + } % 1000000000; Some(nanos) } _ => None, diff --git a/core/src/evaluation/expressions/tests/list_functions.rs b/core/src/evaluation/expressions/tests/list_functions.rs index aaa793a..7b76045 100644 --- a/core/src/evaluation/expressions/tests/list_functions.rs +++ b/core/src/evaluation/expressions/tests/list_functions.rs @@ -4,7 +4,8 @@ use crate::evaluation::variable_value::float::Float; use crate::evaluation::variable_value::integer::Integer; use crate::evaluation::variable_value::VariableValue; use crate::evaluation::{ - EvaluationError, ExpressionEvaluationContext, ExpressionEvaluator, InstantQueryClock, + EvaluationError, ExpressionEvaluationContext, ExpressionEvaluator, FunctionError, + FunctionEvaluationError, InstantQueryClock, }; use crate::in_memory_index::in_memory_result_index::InMemoryResultIndex; @@ -52,14 +53,12 @@ async fn test_list_tail_multiple_arguments() { .await .unwrap_err(); - let _err = Box::new(EvaluationError::InvalidArgumentCount("tail".to_string())); - assert!(matches!( result, - EvaluationError::FunctionError { + EvaluationError::FunctionError(FunctionError { function_name: _name, - error: _err - } + error: FunctionEvaluationError::InvalidArgumentCount + }) )); } @@ -150,14 +149,12 @@ async fn test_list_reverse_multiple_arguments() { .await .unwrap_err(); - let _err = Box::new(EvaluationError::InvalidArgumentCount("reverse".to_string())); - assert!(matches!( result, - EvaluationError::FunctionError { - function_name: _name, - error: _err - } + EvaluationError::FunctionError(FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + }) )); } @@ -239,14 +236,12 @@ async fn test_list_range_invalid_arg_count() { .await .unwrap_err(); - let _err = Box::new(EvaluationError::InvalidArgumentCount("range".to_string())); - assert!(matches!( result, - EvaluationError::FunctionError { - function_name: _name, - error: _err - } + EvaluationError::FunctionError(FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + }) )); let expr = "range(2)"; @@ -258,9 +253,9 @@ async fn test_list_range_invalid_arg_count() { assert!(matches!( result, - EvaluationError::FunctionError { - function_name: _name, - error: _err - } + EvaluationError::FunctionError(FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + }) )); } diff --git a/core/src/evaluation/expressions/tests/list_indexing.rs b/core/src/evaluation/expressions/tests/list_indexing.rs index b8d1457..d5d0f90 100644 --- a/core/src/evaluation/expressions/tests/list_indexing.rs +++ b/core/src/evaluation/expressions/tests/list_indexing.rs @@ -1,338 +1,340 @@ -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::FunctionRegistry; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, InstantQueryClock}; -use crate::evaluation::{ExpressionEvaluationContext, ExpressionEvaluator}; -use crate::in_memory_index::in_memory_result_index::InMemoryResultIndex; -use std::sync::Arc; - -#[tokio::test] -async fn test_list_indexing() { - let expr = "[5,1,7][1]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::Integer(Integer::from(1)) - ); -} - -#[tokio::test] -async fn test_list_indexing_negative_index() { - let expr = "[5,1,7,12,-3423,79][-3]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::Integer(Integer::from(12)) - ); -} - -#[tokio::test] -async fn test_list_indexing_expression_index() { - let expr = "[5,1,7,12,-3423,79][1+2]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::Integer(Integer::from(12)) - ); -} - -#[tokio::test] -async fn test_list_indexing_range_index() { - let expr = "[5,1,7,12,-3423,79][2..5]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(7)), - VariableValue::Integer(Integer::from(12)), - VariableValue::Integer(Integer::from(-3423)) - ]) - ); -} - -#[tokio::test] -async fn test_list_indexing_range_index_unbounded_end() { - let expr = "[5,1,7,12,-3423,79][2..]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(7)), - VariableValue::Integer(Integer::from(12)), - VariableValue::Integer(Integer::from(-3423)), - VariableValue::Integer(Integer::from(79)) - ]) - ); -} - -#[tokio::test] -async fn test_list_indexing_range_index_unbounded_start() { - let expr = "[5,1,7,12,-3423,79][..4]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(5)), - VariableValue::Integer(Integer::from(1)), - VariableValue::Integer(Integer::from(7)), - VariableValue::Integer(Integer::from(12)), - ]) - ); -} - -#[tokio::test] -async fn test_list_indexing_range_index_out_of_bound_slicing() { - let expr = "[5,1,7,12,-3423,79][1..10]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(1)), - VariableValue::Integer(Integer::from(7)), - VariableValue::Integer(Integer::from(12)), - VariableValue::Integer(Integer::from(-3423)), - VariableValue::Integer(Integer::from(79)), - ]) - ); -} - -#[tokio::test] -async fn test_list_indexing_out_of_bound_indexing() { - let expr = "[5,1,7,12,-3423,79][32]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::Null - ); -} - -#[tokio::test] -async fn test_list_indexing_string_indexing() { - let expr = "[5,1,7,12,-3423,79]['abc']"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert!(matches!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap_err(), - EvaluationError::InvalidType - )); -} - -#[tokio::test] -async fn test_list_indexing_null_indexing() { - let expr = "[5,1,7,12,-3423,79][null]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert!(matches!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap_err(), - EvaluationError::InvalidType - )); -} - -#[tokio::test] -async fn test_list_indexing_float_indexing() { - let expr = "[5,1,7,12,-3423,79][-1.5]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert!(matches!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap_err(), - EvaluationError::InvalidType - )); -} - -#[tokio::test] -async fn test_list_indexing_range_index_negative_start_bound() { - let expr = "[0,1,2,3,4,5,6,7,8,9,10][-7..]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(8)), - VariableValue::Integer(Integer::from(9)), - VariableValue::Integer(Integer::from(10)), - ]) - ); -} - -#[tokio::test] -async fn test_list_indexing_range_index_negative_end_bound() { - let expr = "[0,1,2,3,4,5,6,7,8,9,10][..-5]"; - - let expr = drasi_query_cypher::parse_expression(expr).unwrap(); - - let function_registry = Arc::new(FunctionRegistry::new()); - let ari = Arc::new(InMemoryResultIndex::new()); - let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); - - let variables = QueryVariables::new(); - - let context = - ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); - assert_eq!( - evaluator - .evaluate_expression(&context, &expr) - .await - .unwrap(), - VariableValue::List(vec![ - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(1)), - VariableValue::Integer(Integer::from(2)), - VariableValue::Integer(Integer::from(3)), - VariableValue::Integer(Integer::from(4)), - VariableValue::Integer(Integer::from(5)), - ]) - ); -} +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::FunctionRegistry; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + EvaluationError, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; +use crate::evaluation::{ExpressionEvaluationContext, ExpressionEvaluator}; +use crate::in_memory_index::in_memory_result_index::InMemoryResultIndex; +use std::sync::Arc; + +#[tokio::test] +async fn test_list_indexing() { + let expr = "[5,1,7][1]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::Integer(Integer::from(1)) + ); +} + +#[tokio::test] +async fn test_list_indexing_negative_index() { + let expr = "[5,1,7,12,-3423,79][-3]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::Integer(Integer::from(12)) + ); +} + +#[tokio::test] +async fn test_list_indexing_expression_index() { + let expr = "[5,1,7,12,-3423,79][1+2]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::Integer(Integer::from(12)) + ); +} + +#[tokio::test] +async fn test_list_indexing_range_index() { + let expr = "[5,1,7,12,-3423,79][2..5]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(7)), + VariableValue::Integer(Integer::from(12)), + VariableValue::Integer(Integer::from(-3423)) + ]) + ); +} + +#[tokio::test] +async fn test_list_indexing_range_index_unbounded_end() { + let expr = "[5,1,7,12,-3423,79][2..]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(7)), + VariableValue::Integer(Integer::from(12)), + VariableValue::Integer(Integer::from(-3423)), + VariableValue::Integer(Integer::from(79)) + ]) + ); +} + +#[tokio::test] +async fn test_list_indexing_range_index_unbounded_start() { + let expr = "[5,1,7,12,-3423,79][..4]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(5)), + VariableValue::Integer(Integer::from(1)), + VariableValue::Integer(Integer::from(7)), + VariableValue::Integer(Integer::from(12)), + ]) + ); +} + +#[tokio::test] +async fn test_list_indexing_range_index_out_of_bound_slicing() { + let expr = "[5,1,7,12,-3423,79][1..10]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(1)), + VariableValue::Integer(Integer::from(7)), + VariableValue::Integer(Integer::from(12)), + VariableValue::Integer(Integer::from(-3423)), + VariableValue::Integer(Integer::from(79)), + ]) + ); +} + +#[tokio::test] +async fn test_list_indexing_out_of_bound_indexing() { + let expr = "[5,1,7,12,-3423,79][32]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::Null + ); +} + +#[tokio::test] +async fn test_list_indexing_string_indexing() { + let expr = "[5,1,7,12,-3423,79]['abc']"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert!(matches!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap_err(), + EvaluationError::InvalidType { expected: _ }, + )); +} + +#[tokio::test] +async fn test_list_indexing_null_indexing() { + let expr = "[5,1,7,12,-3423,79][null]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert!(matches!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap_err(), + EvaluationError::InvalidType { expected: _ }, + )); +} + +#[tokio::test] +async fn test_list_indexing_float_indexing() { + let expr = "[5,1,7,12,-3423,79][-1.5]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert!(matches!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap_err(), + EvaluationError::InvalidType { expected: _ }, + )); +} + +#[tokio::test] +async fn test_list_indexing_range_index_negative_start_bound() { + let expr = "[0,1,2,3,4,5,6,7,8,9,10][-7..]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(8)), + VariableValue::Integer(Integer::from(9)), + VariableValue::Integer(Integer::from(10)), + ]) + ); +} + +#[tokio::test] +async fn test_list_indexing_range_index_negative_end_bound() { + let expr = "[0,1,2,3,4,5,6,7,8,9,10][..-5]"; + + let expr = drasi_query_cypher::parse_expression(expr).unwrap(); + + let function_registry = Arc::new(FunctionRegistry::new()); + let ari = Arc::new(InMemoryResultIndex::new()); + let evaluator = ExpressionEvaluator::new(function_registry.clone(), ari.clone()); + + let variables = QueryVariables::new(); + + let context = + ExpressionEvaluationContext::new(&variables, Arc::new(InstantQueryClock::new(0, 0))); + assert_eq!( + evaluator + .evaluate_expression(&context, &expr) + .await + .unwrap(), + VariableValue::List(vec![ + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(1)), + VariableValue::Integer(Integer::from(2)), + VariableValue::Integer(Integer::from(3)), + VariableValue::Integer(Integer::from(4)), + VariableValue::Integer(Integer::from(5)), + ]) + ); +} diff --git a/core/src/evaluation/functions/aggregation/avg.rs b/core/src/evaluation/functions/aggregation/avg.rs index 5ea6e91..d5fb837 100644 --- a/core/src/evaluation/functions/aggregation/avg.rs +++ b/core/src/evaluation/functions/aggregation/avg.rs @@ -1,6 +1,9 @@ use std::{fmt::Debug, sync::Arc}; -use crate::{evaluation::EvaluationError, interface::ResultIndex}; +use crate::{ + evaluation::{FunctionError, FunctionEvaluationError}, + interface::ResultIndex, +}; use async_trait::async_trait; @@ -38,23 +41,44 @@ impl AggregatingFunction for Avg { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("avg".to_string())); + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (sum, count) = match accumulator { Accumulator::Value(accumulator) => match accumulator { ValueAccumulator::Avg { sum, count } => (sum, count), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { *count += 1; - *sum += n.as_f64().unwrap(); + *sum += match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; let avg = *sum / *count as f64; Ok(VariableValue::Float( @@ -63,7 +87,15 @@ impl AggregatingFunction for Avg { } VariableValue::Integer(n) => { *count += 1; - *sum += n.as_i64().unwrap() as f64; + *sum += match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; let avg = *sum / *count as f64; Ok(VariableValue::Float( @@ -89,7 +121,10 @@ impl AggregatingFunction for Avg { Float::from_f64(avg).unwrap_or_default(), )) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -98,22 +133,43 @@ impl AggregatingFunction for Avg { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Avg".to_string())); + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (sum, count) = match accumulator { Accumulator::Value(accumulator) => match accumulator { ValueAccumulator::Avg { sum, count } => (sum, count), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { *count -= 1; - *sum -= n.as_f64().unwrap(); + *sum -= match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; if *count == 0 { return Ok(VariableValue::Float( @@ -129,7 +185,15 @@ impl AggregatingFunction for Avg { } VariableValue::Integer(n) => { *count -= 1; - *sum -= n.as_i64().unwrap() as f64; + *sum -= match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; if *count == 0 { return Ok(VariableValue::Float( @@ -167,7 +231,10 @@ impl AggregatingFunction for Avg { Float::from_f64(avg).unwrap_or_default(), )) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -176,16 +243,29 @@ impl AggregatingFunction for Avg { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Avg".to_string())); + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (sum, count) = match accumulator { Accumulator::Value(accumulator) => match accumulator { ValueAccumulator::Avg { sum, count } => (sum, count), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; if *count == 0 { @@ -197,14 +277,21 @@ impl AggregatingFunction for Avg { let avg = *sum / *count as f64; match &args[0] { - VariableValue::Float(_) => Ok(VariableValue::Float(Float::from_f64(avg).unwrap())), - VariableValue::Integer(_) => Ok(VariableValue::Float(Float::from_f64(avg).unwrap())), + VariableValue::Float(_) => Ok(VariableValue::Float( + Float::from_f64(avg).unwrap_or_default(), + )), + VariableValue::Integer(_) => Ok(VariableValue::Float( + Float::from_f64(avg).unwrap_or_default(), + )), VariableValue::Duration(_) => Ok(VariableValue::Duration(Duration::new( ChronoDuration::milliseconds(avg as i64), 0, 0, ))), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Avg".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/aggregation/count.rs b/core/src/evaluation/functions/aggregation/count.rs index d2a4f0c..5d87b95 100644 --- a/core/src/evaluation/functions/aggregation/count.rs +++ b/core/src/evaluation/functions/aggregation/count.rs @@ -1,112 +1,146 @@ -use std::{fmt::Debug, sync::Arc}; - -use crate::{evaluation::EvaluationError, interface::ResultIndex}; - -use async_trait::async_trait; - -use drasi_query_ast::ast; - -use crate::evaluation::{ - variable_value::integer::Integer, variable_value::VariableValue, ExpressionEvaluationContext, -}; - -use super::{super::AggregatingFunction, Accumulator, ValueAccumulator}; - -pub struct Count {} - -#[async_trait] -impl AggregatingFunction for Count { - fn initialize_accumulator( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - _grouping_keys: &Vec, - _index: Arc, - ) -> Accumulator { - Accumulator::Value(ValueAccumulator::Count { value: 0 }) - } - - fn accumulator_is_lazy(&self) -> bool { - false - } - - async fn apply( - &self, - _context: &ExpressionEvaluationContext, - args: Vec, - accumulator: &mut Accumulator, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Count".to_string())); - } - - let value = match accumulator { - Accumulator::Value(accumulator) => match accumulator { - super::ValueAccumulator::Count { value } => value, - _ => return Err(EvaluationError::InvalidType), - }, - _ => return Err(EvaluationError::InvalidType), - }; - - match &args[0] { - VariableValue::Null => Ok(VariableValue::Integer(Integer::from(*value))), - _ => { - *value += 1; - Ok(VariableValue::Integer(Integer::from(*value))) - } - } - } - - async fn revert( - &self, - _context: &ExpressionEvaluationContext, - args: Vec, - accumulator: &mut Accumulator, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Count".to_string())); - } - let value = match accumulator { - Accumulator::Value(accumulator) => match accumulator { - super::ValueAccumulator::Count { value } => value, - _ => return Err(EvaluationError::InvalidType), - }, - _ => return Err(EvaluationError::InvalidType), - }; - - // if *value == 0 { - // println!("Count is already 0"); - // return Ok(VariableValue::Integer(Integer::from(*value))); - // } - - match &args[0] { - VariableValue::Null => Ok(VariableValue::Integer(Integer::from(*value))), - _ => { - *value -= 1; - Ok(VariableValue::Integer(Integer::from(*value))) - } - } - } - - async fn snapshot( - &self, - _context: &ExpressionEvaluationContext, - _args: Vec, - accumulator: &Accumulator, - ) -> Result { - let value = match accumulator { - Accumulator::Value(accumulator) => match accumulator { - super::ValueAccumulator::Count { value } => value, - _ => return Err(EvaluationError::InvalidType), - }, - _ => return Err(EvaluationError::InvalidType), - }; - Ok(VariableValue::Integer(Integer::from(*value))) - } -} - -impl Debug for Count { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Count") - } -} +use std::{fmt::Debug, sync::Arc}; + +use crate::{ + evaluation::{FunctionError, FunctionEvaluationError}, + interface::ResultIndex, +}; + +use async_trait::async_trait; + +use drasi_query_ast::ast; + +use crate::evaluation::{ + variable_value::integer::Integer, variable_value::VariableValue, ExpressionEvaluationContext, +}; + +use super::{super::AggregatingFunction, Accumulator, ValueAccumulator}; + +pub struct Count {} + +#[async_trait] +impl AggregatingFunction for Count { + fn initialize_accumulator( + &self, + _context: &ExpressionEvaluationContext, + _expression: &ast::FunctionExpression, + _grouping_keys: &Vec, + _index: Arc, + ) -> Accumulator { + Accumulator::Value(ValueAccumulator::Count { value: 0 }) + } + + fn accumulator_is_lazy(&self) -> bool { + false + } + + async fn apply( + &self, + _context: &ExpressionEvaluationContext, + args: Vec, + accumulator: &mut Accumulator, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + + let value = match accumulator { + Accumulator::Value(accumulator) => match accumulator { + super::ValueAccumulator::Count { value } => value, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }; + + match &args[0] { + VariableValue::Null => Ok(VariableValue::Integer(Integer::from(*value))), + _ => { + *value += 1; + Ok(VariableValue::Integer(Integer::from(*value))) + } + } + } + + async fn revert( + &self, + _context: &ExpressionEvaluationContext, + args: Vec, + accumulator: &mut Accumulator, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + let value = match accumulator { + Accumulator::Value(accumulator) => match accumulator { + super::ValueAccumulator::Count { value } => value, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }; + + match &args[0] { + VariableValue::Null => Ok(VariableValue::Integer(Integer::from(*value))), + _ => { + *value -= 1; + Ok(VariableValue::Integer(Integer::from(*value))) + } + } + } + + async fn snapshot( + &self, + _context: &ExpressionEvaluationContext, + _args: Vec, + accumulator: &Accumulator, + ) -> Result { + let value = match accumulator { + Accumulator::Value(accumulator) => match accumulator { + super::ValueAccumulator::Count { value } => value, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }, + _ => { + return Err(FunctionError { + function_name: "Count".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }; + Ok(VariableValue::Integer(Integer::from(*value))) + } +} + +impl Debug for Count { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Count") + } +} diff --git a/core/src/evaluation/functions/aggregation/last.rs b/core/src/evaluation/functions/aggregation/last.rs index e33e0bc..3c991cc 100644 --- a/core/src/evaluation/functions/aggregation/last.rs +++ b/core/src/evaluation/functions/aggregation/last.rs @@ -1,6 +1,10 @@ use std::{fmt::Debug, sync::Arc}; -use crate::{evaluation::EvaluationError, interface::ResultIndex, models::ElementValue}; +use crate::{ + evaluation::{FunctionError, FunctionEvaluationError}, + interface::ResultIndex, + models::ElementValue, +}; use async_trait::async_trait; @@ -33,22 +37,42 @@ impl AggregatingFunction for Last { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Last".to_string())); + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let value = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Value(value) => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; *value = match (&args[0]).try_into() { Ok(value) => value, - Err(_) => return Err(EvaluationError::InvalidType), + Err(_) => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "ElementValue".to_string(), + }, + }) + } }; Ok((&value.clone()).into()) @@ -59,16 +83,29 @@ impl AggregatingFunction for Last { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Last".to_string())); + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let value = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Value(value) => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; *value = ElementValue::Null; @@ -80,13 +117,23 @@ impl AggregatingFunction for Last { _context: &ExpressionEvaluationContext, _args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { let value = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Value(value) => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Last".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; Ok((&value.clone()).into()) } diff --git a/core/src/evaluation/functions/aggregation/linear_gradient.rs b/core/src/evaluation/functions/aggregation/linear_gradient.rs index 3f3b383..a626233 100644 --- a/core/src/evaluation/functions/aggregation/linear_gradient.rs +++ b/core/src/evaluation/functions/aggregation/linear_gradient.rs @@ -1,6 +1,9 @@ use std::{fmt::Debug, sync::Arc}; -use crate::{evaluation::EvaluationError, interface::ResultIndex}; +use crate::{ + evaluation::{FunctionError, FunctionEvaluationError}, + interface::ResultIndex, +}; use async_trait::async_trait; @@ -43,11 +46,12 @@ impl AggregatingFunction for LinearGradient { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "linearGradient".to_string(), - )); + return Err(FunctionError { + function_name: "linearGradient".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (count, mean_x, mean_y, m2, cov) = match accumulator { @@ -59,9 +63,19 @@ impl AggregatingFunction for LinearGradient { m2, cov, } => (count, mean_x, mean_y, m2, cov), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; if let VariableValue::Null = args[0] { @@ -72,8 +86,8 @@ impl AggregatingFunction for LinearGradient { return Ok(VariableValue::Null); } - let x = extract_parameter(&args[0])?; - let y = extract_parameter(&args[1])?; + let x = extract_parameter(&args[0], 0)?; + let y = extract_parameter(&args[1], 1)?; *count += 1; let delta_x = x - *mean_x; @@ -100,11 +114,12 @@ impl AggregatingFunction for LinearGradient { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "linearGradient".to_string(), - )); + return Err(FunctionError { + function_name: "linearGradient".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (count, mean_x, mean_y, m2, cov) = match accumulator { @@ -116,9 +131,19 @@ impl AggregatingFunction for LinearGradient { m2, cov, } => (count, mean_x, mean_y, m2, cov), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; if let VariableValue::Null = args[0] { @@ -129,8 +154,8 @@ impl AggregatingFunction for LinearGradient { return Ok(VariableValue::Null); } - let x = extract_parameter(&args[0])?; - let y = extract_parameter(&args[1])?; + let x = extract_parameter(&args[0], 0)?; + let y = extract_parameter(&args[1], 1)?; *count -= 1; @@ -166,11 +191,12 @@ impl AggregatingFunction for LinearGradient { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "linearGradient".to_string(), - )); + return Err(FunctionError { + function_name: "linearGradient".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let (count, _mean_x, _mean_y, m2, cov) = match accumulator { @@ -182,9 +208,19 @@ impl AggregatingFunction for LinearGradient { m2, cov, } => (count, mean_x, mean_y, m2, cov), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; if *count == 0 { @@ -203,17 +239,38 @@ impl AggregatingFunction for LinearGradient { } } -fn extract_parameter(p: &VariableValue) -> Result { +fn extract_parameter(p: &VariableValue, index: u64) -> Result { let result = match p { - VariableValue::Float(n) => n.as_f64().unwrap(), - VariableValue::Integer(n) => n.as_i64().unwrap() as f64, + VariableValue::Float(n) => match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + VariableValue::Integer(n) => match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, VariableValue::Duration(d) => d.duration().num_milliseconds() as f64, VariableValue::LocalDateTime(l) => l.and_utc().timestamp_millis() as f64, VariableValue::ZonedDateTime(z) => z.datetime().timestamp_millis() as f64, VariableValue::Date(d) => d.and_time(NaiveTime::MIN).and_utc().timestamp_millis() as f64, VariableValue::LocalTime(l) => l.num_seconds_from_midnight() as f64, VariableValue::ZonedTime(z) => z.time().num_seconds_from_midnight() as f64, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "LinearGradient".to_string(), + error: FunctionEvaluationError::InvalidArgument(index as usize), + }) + } }; Ok(result) diff --git a/core/src/evaluation/functions/aggregation/max.rs b/core/src/evaluation/functions/aggregation/max.rs index 98da101..0a9fd27 100644 --- a/core/src/evaluation/functions/aggregation/max.rs +++ b/core/src/evaluation/functions/aggregation/max.rs @@ -6,7 +6,7 @@ use crate::{ variable_value::{ duration::Duration, zoned_datetime::ZonedDateTime, zoned_time::ZonedTime, }, - EvaluationError, + {FunctionError, FunctionEvaluationError}, }, interface::ResultIndex, }; @@ -19,7 +19,7 @@ use crate::evaluation::{ variable_value::float::Float, variable_value::VariableValue, ExpressionEvaluationContext, }; -use chrono::{DateTime, Duration as ChronoDuration, FixedOffset}; +use chrono::{DateTime, Duration as ChronoDuration, LocalResult}; use super::{super::AggregatingFunction, lazy_sorted_set::LazySortedSet, Accumulator}; @@ -51,61 +51,114 @@ impl AggregatingFunction for Max { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Max".to_string())); + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } log::info!("Applying Max with args: {:?}", args); let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - let value = n.as_f64().unwrap(); + let value = match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.insert(value * -1.0).await; - match accumulator.get_head().await? { - Some(head) => match Float::from_f64(head * -1.0) { - Some(f) => Ok(VariableValue::Float(f)), - None => Err(EvaluationError::InvalidState), - }, - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => { + Ok(VariableValue::Float(match Float::from_f64(head * -1.0) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + })) + } + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Integer(n) => { - let value = n.as_i64().unwrap(); + let value = match n.as_i64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.insert((value as f64) * -1.0).await; - match accumulator.get_head().await? { - Some(head) => match Float::from_f64(head * -1.0) { - Some(f) => Ok(VariableValue::Float(f)), - None => Err(EvaluationError::InvalidState), - }, - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => { + Ok(VariableValue::Float(match Float::from_f64(head * -1.0) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + })) + } + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedDateTime(zdt) => { let value = zdt.datetime().timestamp_millis() as f64; accumulator.insert(value * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedDateTime( ZonedDateTime::from_epoch_millis((head * -1.0) as u64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Duration(d) => { let value = d.duration().num_milliseconds() as f64; accumulator.insert(value * -1.0).await; - let head = accumulator.get_head().await?; - match head { - Some(head) => Ok(VariableValue::Duration(Duration::new( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Duration(Duration::new( ChronoDuration::milliseconds((head * -1.0) as i64), 0, 0, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Date(d) => { @@ -113,11 +166,15 @@ impl AggregatingFunction for Max { let reference_date = *temporal_constants::EPOCH_NAIVE_DATE; let days_since_epoch = d.signed_duration_since(reference_date).num_days() as f64; accumulator.insert(days_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Date( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Date( reference_date + ChronoDuration::days((head * -1.0) as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalTime(t) => { @@ -126,44 +183,70 @@ impl AggregatingFunction for Max { t.signed_duration_since(reference_time).num_milliseconds() as f64; accumulator.insert(duration_since_midnight * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalTime( reference_time + ChronoDuration::milliseconds((head * -1.0) as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalDateTime(dt) => { let duration_since_epoch = dt.and_utc().timestamp_millis() as f64; accumulator.insert(duration_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalDateTime( DateTime::from_timestamp_millis(head as i64 * -1.0 as i64) .unwrap_or_default() .naive_local(), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedTime(t) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*t.time()) .and_local_timezone(*t.offset()) - .unwrap(); + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let duration_since_epoch = epoch_datetime.timestamp_millis() as f64; accumulator.insert(duration_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedTime(ZonedTime::new( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedTime(ZonedTime::new( (epoch_datetime + ChronoDuration::milliseconds((head * -1.0) as i64)) .time(), - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -172,52 +255,112 @@ impl AggregatingFunction for Max { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Max".to_string())); + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - let value = n.as_f64().unwrap(); + let value = match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.remove(value * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Float(Float::from_f64(head * -1.0).unwrap())), - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => { + Ok(VariableValue::Float(match Float::from_f64(head * -1.0) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + })) + } + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Integer(n) => { - let value = n.as_i64().unwrap(); + let value = match n.as_i64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.remove((value as f64) * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Float(Float::from_f64(head * -1.0).unwrap())), - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => { + Ok(VariableValue::Float(match Float::from_f64(head * -1.0) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + })) + } + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedDateTime(zdt) => { let value = zdt.datetime().timestamp_millis() as f64; accumulator.remove(value * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedDateTime( ZonedDateTime::from_epoch_millis((head * -1.0) as u64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Duration(d) => { let value = d.duration().num_milliseconds() as f64; accumulator.remove(value * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Duration(Duration::new( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Duration(Duration::new( ChronoDuration::milliseconds((head * -1.0) as i64), 0, 0, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Date(d) => { @@ -225,11 +368,15 @@ impl AggregatingFunction for Max { let reference_date = *temporal_constants::EPOCH_NAIVE_DATE; let days_since_epoch = d.signed_duration_since(reference_date).num_days() as f64; accumulator.remove(days_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Date( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Date( reference_date + ChronoDuration::days((head * -1.0) as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalTime(t) => { @@ -238,44 +385,70 @@ impl AggregatingFunction for Max { t.signed_duration_since(reference_time).num_milliseconds() as f64; accumulator.remove(duration_since_midnight * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalTime( reference_time + ChronoDuration::milliseconds((head * -1.0) as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalDateTime(dt) => { let duration_since_epoch = dt.and_utc().timestamp_millis() as f64; accumulator.remove(duration_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalDateTime( DateTime::from_timestamp_millis(head as i64 * -1.0 as i64) .unwrap_or_default() .naive_local(), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedTime(t) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*t.time()) .and_local_timezone(*t.offset()) - .unwrap(); + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let duration_since_epoch = epoch_datetime.timestamp_millis() as f64; accumulator.remove(duration_since_epoch * -1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedTime(ZonedTime::new( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedTime(ZonedTime::new( (epoch_datetime + ChronoDuration::milliseconds((head * -1.0) as i64)) .time(), - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -284,23 +457,45 @@ impl AggregatingFunction for Max { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Max".to_string())); + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; - let value = match accumulator.get_head().await? { - Some(head) => head * -1.0, - None => return Ok(VariableValue::Null), + let value = match accumulator.get_head().await { + Ok(Some(head)) => head * -1.0, + Ok(None) => return Ok(VariableValue::Null), + Err(e) => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } }; return match &args[0] { - VariableValue::Float(_) => Ok(VariableValue::Float(Float::from_f64(value).unwrap())), + VariableValue::Float(_) => Ok(VariableValue::Float(match Float::from_f64(value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), VariableValue::Integer(_) => Ok(VariableValue::Integer((value as i64).into())), VariableValue::ZonedDateTime(_) => Ok(VariableValue::ZonedDateTime( ZonedDateTime::from_epoch_millis(value as u64), @@ -329,17 +524,31 @@ impl AggregatingFunction for Max { )), VariableValue::ZonedTime(_) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*temporal_constants::MIDNIGHT_NAIVE_TIME) - .and_local_timezone(FixedOffset::east_opt(0).unwrap()) - .unwrap(); + .and_local_timezone(*temporal_constants::UTC_FIXED_OFFSET) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; Ok(VariableValue::ZonedTime(ZonedTime::new( (epoch_datetime + ChronoDuration::milliseconds(value as i64)).time(), - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, ))) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Max".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), }; } } diff --git a/core/src/evaluation/functions/aggregation/min.rs b/core/src/evaluation/functions/aggregation/min.rs index 0f306c4..dd55924 100644 --- a/core/src/evaluation/functions/aggregation/min.rs +++ b/core/src/evaluation/functions/aggregation/min.rs @@ -2,7 +2,8 @@ use std::{fmt::Debug, sync::Arc}; use crate::{ evaluation::{ - temporal_constants, variable_value::zoned_datetime::ZonedDateTime, EvaluationError, + temporal_constants, variable_value::zoned_datetime::ZonedDateTime, FunctionError, + FunctionEvaluationError, }, interface::ResultIndex, }; @@ -18,7 +19,7 @@ use crate::evaluation::{ }; use super::{super::AggregatingFunction, lazy_sorted_set::LazySortedSet, Accumulator}; -use chrono::{DateTime, Duration as ChronoDuration, FixedOffset, NaiveTime}; +use chrono::{offset::LocalResult, DateTime, Duration as ChronoDuration}; #[derive(Clone)] pub struct Min {} @@ -48,114 +49,190 @@ impl AggregatingFunction for Min { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Min".to_string())); + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - let value = n.as_f64().unwrap(); + let value = match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.insert(value).await; - match accumulator.get_head().await? { - Some(head) => match Float::from_f64(head) { + match accumulator.get_head().await { + Ok(Some(head)) => match Float::from_f64(head) { Some(f) => Ok(VariableValue::Float(f)), - None => Err(EvaluationError::InvalidState), + None => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }), }, - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Integer(n) => { - let value = n.as_i64().unwrap(); + let value = match n.as_i64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.insert(value as f64).await; - match accumulator.get_head().await? { - Some(head) => match Float::from_f64(head) { + match accumulator.get_head().await { + Ok(Some(head)) => match Float::from_f64(head) { Some(f) => Ok(VariableValue::Float(f)), - None => Err(EvaluationError::InvalidState), + None => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }), }, - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedDateTime(zdt) => { let value = zdt.datetime().timestamp_millis() as f64; accumulator.insert(value).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedDateTime( ZonedDateTime::from_epoch_millis(head as u64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Duration(d) => { let value = d.duration().num_milliseconds() as f64; accumulator.insert(value).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Duration(Duration::new( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Duration(Duration::new( ChronoDuration::milliseconds(head as i64), 0, 0, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Date(d) => { let reference_date = *temporal_constants::EPOCH_NAIVE_DATE; let days_since_epoch = d.signed_duration_since(reference_date).num_days() as f64; accumulator.insert(days_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Date( - reference_date + ChronoDuration::days((head) as i64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Date( + reference_date + ChronoDuration::days(head as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalTime(t) => { - let reference_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let reference_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; let seconds_since_midnight = t.signed_duration_since(reference_time).num_milliseconds() as f64; accumulator.insert(seconds_since_midnight).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalTime( - reference_time + ChronoDuration::milliseconds((head) as i64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalTime( + reference_time + ChronoDuration::milliseconds(head as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalDateTime(dt) => { let duration_since_epoch = dt.and_utc().timestamp_millis() as f64; accumulator.insert(duration_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalDateTime( DateTime::from_timestamp_millis(head as i64) .unwrap_or_default() .naive_local(), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedTime(t) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*t.time()) .and_local_timezone(*t.offset()) - .unwrap(); + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let duration_since_epoch = epoch_datetime.timestamp_millis() as f64; accumulator.insert(duration_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedTime(ZonedTime::new( - (epoch_datetime + ChronoDuration::milliseconds((head) as i64)).time(), - FixedOffset::east_opt(0).unwrap(), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedTime(ZonedTime::new( + (epoch_datetime + ChronoDuration::milliseconds(head as i64)).time(), + *temporal_constants::UTC_FIXED_OFFSET, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -164,52 +241,104 @@ impl AggregatingFunction for Min { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Min".to_string())); + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - let value = n.as_f64().unwrap(); + let value = match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.remove(value).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Float(Float::from_f64(head).unwrap())), - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => match Float::from_f64(head) { + Some(f) => Ok(VariableValue::Float(f)), + None => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }), + }, + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Integer(n) => { - let value = n.as_i64().unwrap(); + let value = match n.as_i64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; accumulator.remove((value as f64) * 1.0).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Float(Float::from_f64(head).unwrap())), - None => Ok(VariableValue::Null), + match accumulator.get_head().await { + Ok(Some(head)) => match Float::from_f64(head) { + Some(f) => Ok(VariableValue::Float(f)), + None => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }), + }, + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedDateTime(zdt) => { let value = zdt.datetime().timestamp_millis() as f64; accumulator.remove(value).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedDateTime( - ZonedDateTime::from_epoch_millis((head) as u64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedDateTime( + ZonedDateTime::from_epoch_millis(head as u64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Duration(d) => { let value = d.duration().num_milliseconds() as f64; accumulator.remove(value).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Duration(Duration::new( - ChronoDuration::milliseconds((head) as i64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Duration(Duration::new( + ChronoDuration::milliseconds(head as i64), 0, 0, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Date(d) => { @@ -217,11 +346,15 @@ impl AggregatingFunction for Min { let reference_date = *temporal_constants::EPOCH_NAIVE_DATE; let days_since_epoch = d.signed_duration_since(reference_date).num_days() as f64; accumulator.remove(days_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::Date( - reference_date + ChronoDuration::days((head) as i64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::Date( + reference_date + ChronoDuration::days(head as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalTime(t) => { @@ -230,43 +363,69 @@ impl AggregatingFunction for Min { t.signed_duration_since(reference_time).num_milliseconds() as f64; accumulator.remove(duration_since_midnight).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalTime( - reference_time + ChronoDuration::milliseconds((head) as i64), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalTime( + reference_time + ChronoDuration::milliseconds(head as i64), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::LocalDateTime(dt) => { let duration_since_epoch = dt.and_utc().timestamp_millis() as f64; accumulator.remove(duration_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::LocalDateTime( + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::LocalDateTime( DateTime::from_timestamp_millis(head as i64) .unwrap_or_default() .naive_local(), )), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::ZonedTime(t) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*t.time()) .and_local_timezone(*t.offset()) - .unwrap(); + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let duration_since_epoch = epoch_datetime.timestamp_millis() as f64; accumulator.remove(duration_since_epoch).await; - match accumulator.get_head().await? { - Some(head) => Ok(VariableValue::ZonedTime(ZonedTime::new( - (epoch_datetime + ChronoDuration::milliseconds((head) as i64)).time(), - FixedOffset::east_opt(0).unwrap(), + match accumulator.get_head().await { + Ok(Some(head)) => Ok(VariableValue::ZonedTime(ZonedTime::new( + (epoch_datetime + ChronoDuration::milliseconds(head as i64)).time(), + *temporal_constants::UTC_FIXED_OFFSET, ))), - None => Ok(VariableValue::Null), + Ok(None) => Ok(VariableValue::Null), + Err(e) => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }), } } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -275,23 +434,45 @@ impl AggregatingFunction for Min { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Min".to_string())); + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::LazySortedSet(accumulator) => accumulator, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; - let value = match accumulator.get_head().await? { - Some(head) => head, - None => return Ok(VariableValue::Null), + let value = match accumulator.get_head().await { + Ok(Some(head)) => head, + Ok(None) => return Ok(VariableValue::Null), + Err(e) => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } }; return match &args[0] { - VariableValue::Float(_) => Ok(VariableValue::Float(Float::from_f64(value).unwrap())), + VariableValue::Float(_) => Ok(VariableValue::Float(match Float::from_f64(value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), VariableValue::Integer(_) => Ok(VariableValue::Integer((value as i64).into())), VariableValue::ZonedDateTime(_) => Ok(VariableValue::ZonedDateTime( ZonedDateTime::from_epoch_millis(value as u64), @@ -320,17 +501,31 @@ impl AggregatingFunction for Min { )), VariableValue::ZonedTime(_) => { let epoch_date = *temporal_constants::EPOCH_NAIVE_DATE; - let epoch_datetime = epoch_date + let epoch_datetime = match epoch_date .and_time(*temporal_constants::MIDNIGHT_NAIVE_TIME) - .and_local_timezone(FixedOffset::east_opt(0).unwrap()) - .unwrap(); + .and_local_timezone(*temporal_constants::UTC_FIXED_OFFSET) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; Ok(VariableValue::ZonedTime(ZonedTime::new( (epoch_datetime + ChronoDuration::milliseconds(value as i64)).time(), - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, ))) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Min".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), }; } } diff --git a/core/src/evaluation/functions/aggregation/sum.rs b/core/src/evaluation/functions/aggregation/sum.rs index f3680a5..e248b52 100644 --- a/core/src/evaluation/functions/aggregation/sum.rs +++ b/core/src/evaluation/functions/aggregation/sum.rs @@ -1,6 +1,9 @@ use std::{fmt::Debug, sync::Arc}; -use crate::{evaluation::EvaluationError, interface::ResultIndex}; +use crate::{ + evaluation::{FunctionError, FunctionEvaluationError}, + interface::ResultIndex, +}; use async_trait::async_trait; @@ -38,27 +41,72 @@ impl AggregatingFunction for Sum { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Sum".to_string())); + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Sum { value } => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - *accumulator += n.as_f64().unwrap(); - Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())) + *accumulator += match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) } VariableValue::Integer(n) => { - *accumulator += n.as_i64().unwrap() as f64; - Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())) + *accumulator += match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) } VariableValue::Duration(d) => { *accumulator += d.duration().num_milliseconds() as f64; @@ -68,8 +116,19 @@ impl AggregatingFunction for Sum { 0, ))) } - VariableValue::Null => Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())), - _ => Err(EvaluationError::InvalidType), + VariableValue::Null => Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), + _ => Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -78,26 +137,71 @@ impl AggregatingFunction for Sum { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Sum".to_string())); + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Sum { value } => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(n) => { - *accumulator -= n.as_f64().unwrap(); - Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())) + *accumulator -= match n.as_f64() { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) } VariableValue::Integer(n) => { - *accumulator -= n.as_i64().unwrap() as f64; - Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())) + *accumulator -= match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) } VariableValue::Duration(d) => { *accumulator -= d.duration().num_milliseconds() as f64; @@ -107,8 +211,19 @@ impl AggregatingFunction for Sum { 0, ))) } - VariableValue::Null => Ok(VariableValue::Float(Float::from_f64(*accumulator).unwrap())), - _ => Err(EvaluationError::InvalidType), + VariableValue::Null => Ok(VariableValue::Float(match Float::from_f64(*accumulator) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), + _ => Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } @@ -117,24 +232,53 @@ impl AggregatingFunction for Sum { _context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("Sum".to_string())); + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let accumulator_value = match accumulator { Accumulator::Value(accumulator) => match accumulator { super::ValueAccumulator::Sum { value } => value, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } }; match &args[0] { VariableValue::Float(_) => Ok(VariableValue::Float( - Float::from_f64(*accumulator_value).unwrap(), + match Float::from_f64(*accumulator_value) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), VariableValue::Integer(_) => Ok(VariableValue::Float( - Float::from_f64(*accumulator_value).unwrap(), + match Float::from_f64(*accumulator_value) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), VariableValue::Duration(_) => Ok(VariableValue::Duration(Duration::new( ChronoDuration::milliseconds(*accumulator_value as i64), @@ -142,9 +286,20 @@ impl AggregatingFunction for Sum { 0, ))), VariableValue::Null => Ok(VariableValue::Float( - Float::from_f64(*accumulator_value).unwrap(), + match Float::from_f64(*accumulator_value) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: "Sum".to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/context_mutators.rs b/core/src/evaluation/functions/context_mutators.rs index bd64233..d0c37c4 100644 --- a/core/src/evaluation/functions/context_mutators.rs +++ b/core/src/evaluation/functions/context_mutators.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; use drasi_query_ast::ast; -use crate::evaluation::{context::SideEffects, EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{context::SideEffects, ExpressionEvaluationContext, FunctionError}; use super::ContextMutatorFunction; @@ -30,7 +30,7 @@ impl ContextMutatorFunction for RetainHistory { &self, context: &ExpressionEvaluationContext<'a>, _expression: &ast::FunctionExpression, - ) -> Result, EvaluationError> { + ) -> Result, FunctionError> { let mut new_context = context.clone(); match new_context.get_side_effects() { SideEffects::RevertForUpdate => new_context.set_side_effects(SideEffects::Snapshot), diff --git a/core/src/evaluation/functions/cypher_scalar/char_length.rs b/core/src/evaluation/functions/cypher_scalar/char_length.rs index 72db945..c966278 100644 --- a/core/src/evaluation/functions/cypher_scalar/char_length.rs +++ b/core/src/evaluation/functions/cypher_scalar/char_length.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct CharLength {} @@ -15,16 +15,20 @@ impl ScalarFunction for CharLength { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::Integer((s.len() as i64).into())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/cypher_scalar/coalesce.rs b/core/src/evaluation/functions/cypher_scalar/coalesce.rs index f860220..6643b26 100644 --- a/core/src/evaluation/functions/cypher_scalar/coalesce.rs +++ b/core/src/evaluation/functions/cypher_scalar/coalesce.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError}; #[derive(Debug)] pub struct Coalesce {} @@ -15,7 +15,7 @@ impl ScalarFunction for Coalesce { _context: &ExpressionEvaluationContext, _expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { for arg in args { if arg != VariableValue::Null { return Ok(arg); diff --git a/core/src/evaluation/functions/cypher_scalar/head.rs b/core/src/evaluation/functions/cypher_scalar/head.rs index f69f2a5..732b28c 100644 --- a/core/src/evaluation/functions/cypher_scalar/head.rs +++ b/core/src/evaluation/functions/cypher_scalar/head.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Head {} @@ -15,11 +15,12 @@ impl ScalarFunction for Head { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => { @@ -29,7 +30,10 @@ impl ScalarFunction for Head { Ok(l[0].clone()) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/cypher_scalar/last.rs b/core/src/evaluation/functions/cypher_scalar/last.rs index 5394b38..c742b88 100644 --- a/core/src/evaluation/functions/cypher_scalar/last.rs +++ b/core/src/evaluation/functions/cypher_scalar/last.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Last {} @@ -15,11 +15,12 @@ impl ScalarFunction for Last { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => { @@ -29,7 +30,10 @@ impl ScalarFunction for Last { Ok(l[l.len() - 1].clone()) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/cypher_scalar/size.rs b/core/src/evaluation/functions/cypher_scalar/size.rs index 87cbb7c..c375bc2 100644 --- a/core/src/evaluation/functions/cypher_scalar/size.rs +++ b/core/src/evaluation/functions/cypher_scalar/size.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Size {} @@ -15,17 +15,21 @@ impl ScalarFunction for Size { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => Ok(VariableValue::Integer((l.len() as i64).into())), VariableValue::String(s) => Ok(VariableValue::Integer((s.len() as i64).into())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/cypher_scalar/tests/head_tests.rs b/core/src/evaluation/functions/cypher_scalar/tests/head_tests.rs index e2e42d2..bfbb6ad 100644 --- a/core/src/evaluation/functions/cypher_scalar/tests/head_tests.rs +++ b/core/src/evaluation/functions/cypher_scalar/tests/head_tests.rs @@ -6,7 +6,9 @@ use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::cypher_scalar::head::Head; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -57,6 +59,9 @@ async fn test_head_multiple_args() { let result = head.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } diff --git a/core/src/evaluation/functions/cypher_scalar/tests/last_tests.rs b/core/src/evaluation/functions/cypher_scalar/tests/last_tests.rs index 5fe3958..ce2bac3 100644 --- a/core/src/evaluation/functions/cypher_scalar/tests/last_tests.rs +++ b/core/src/evaluation/functions/cypher_scalar/tests/last_tests.rs @@ -6,7 +6,9 @@ use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::cypher_scalar::last::Last; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -57,6 +59,9 @@ async fn test_last_multiple_args() { let result = last.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } diff --git a/core/src/evaluation/functions/cypher_scalar/timestamp.rs b/core/src/evaluation/functions/cypher_scalar/timestamp.rs index 54acd48..c58a845 100644 --- a/core/src/evaluation/functions/cypher_scalar/timestamp.rs +++ b/core/src/evaluation/functions/cypher_scalar/timestamp.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Timestamp {} @@ -13,18 +13,33 @@ impl ScalarFunction for Timestamp { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 0 { - return Err(EvaluationError::InvalidArgumentCount( - "timestamp".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let now = std::time::SystemTime::now(); - let since_epoch = now.duration_since(std::time::UNIX_EPOCH).unwrap(); - Ok(VariableValue::Integer( - (since_epoch.as_millis() as i64).into(), - )) + let since_epoch = match now.duration_since(std::time::UNIX_EPOCH) { + Ok(d) => { + if d.as_millis() > i64::MAX as u128 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }); + } else { + d.as_millis() as i64 + } + } + Err(_e) => { + // This should never happen, since duration_since will ony return an error if the time is before the UNIX_EPOCH + // return a zero duration this case + 0 + } + }; + Ok(VariableValue::Integer((since_epoch).into())) } } diff --git a/core/src/evaluation/functions/cypher_scalar/to_boolean.rs b/core/src/evaluation/functions/cypher_scalar/to_boolean.rs index 79cf992..418d77d 100644 --- a/core/src/evaluation/functions/cypher_scalar/to_boolean.rs +++ b/core/src/evaluation/functions/cypher_scalar/to_boolean.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct ToBoolean {} @@ -15,15 +15,24 @@ impl ScalarFunction for ToBoolean { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(i) => Ok(VariableValue::Bool(i.as_i64().unwrap() != 0)), + VariableValue::Integer(i) => Ok(VariableValue::Bool(match i.as_i64() { + Some(i) => i != 0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), VariableValue::String(s) => { let s = s.to_lowercase(); if (s == "true") || (s == "false") { @@ -33,9 +42,9 @@ impl ScalarFunction for ToBoolean { } } VariableValue::Bool(b) => Ok(VariableValue::Bool(*b)), - _ => Err(EvaluationError::FunctionError { + _ => Err(FunctionError { function_name: expression.name.to_string(), - error: Box::new(EvaluationError::InvalidType), + error: FunctionEvaluationError::InvalidArgument(0), }), } } @@ -51,15 +60,24 @@ impl ScalarFunction for ToBooleanOrNull { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(i) => Ok(VariableValue::Bool(i.as_i64().unwrap() != 0)), + VariableValue::Integer(i) => Ok(VariableValue::Bool(match i.as_i64() { + Some(i) => i != 0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), VariableValue::String(s) => { if (s == "true") || (s == "false") { Ok(VariableValue::Bool(s == "true")) diff --git a/core/src/evaluation/functions/cypher_scalar/to_float.rs b/core/src/evaluation/functions/cypher_scalar/to_float.rs index f62810f..71d7b82 100644 --- a/core/src/evaluation/functions/cypher_scalar/to_float.rs +++ b/core/src/evaluation/functions/cypher_scalar/to_float.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct ToFloat {} @@ -15,17 +15,27 @@ impl ScalarFunction for ToFloat { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(i) => { - Ok(VariableValue::Float((i.as_i64().unwrap() as f64).into())) - } + VariableValue::Integer(i) => Ok(VariableValue::Float( + (match i.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } as f64) + .into(), + )), VariableValue::Float(f) => Ok(VariableValue::Float(f.clone())), VariableValue::String(s) => { if let Ok(i) = s.parse::() { @@ -36,9 +46,9 @@ impl ScalarFunction for ToFloat { Ok(VariableValue::Null) } } - _ => Err(EvaluationError::FunctionError { + _ => Err(FunctionError { function_name: expression.name.to_string(), - error: Box::new(EvaluationError::InvalidType), + error: FunctionEvaluationError::InvalidArgument(0), }), } } @@ -54,17 +64,27 @@ impl ScalarFunction for ToFloatOrNull { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(i) => { - Ok(VariableValue::Float((i.as_i64().unwrap() as f64).into())) - } + VariableValue::Integer(i) => Ok(VariableValue::Float( + (match i.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } as f64) + .into(), + )), VariableValue::Float(f) => Ok(VariableValue::Float(f.clone())), VariableValue::String(s) => { if let Ok(i) = s.parse::() { diff --git a/core/src/evaluation/functions/cypher_scalar/to_integer.rs b/core/src/evaluation/functions/cypher_scalar/to_integer.rs index 669480d..48d1038 100644 --- a/core/src/evaluation/functions/cypher_scalar/to_integer.rs +++ b/core/src/evaluation/functions/cypher_scalar/to_integer.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct ToInteger {} @@ -15,17 +15,27 @@ impl ScalarFunction for ToInteger { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), VariableValue::Integer(i) => Ok(VariableValue::Integer(i.clone())), VariableValue::Float(f) => Ok(VariableValue::Integer( - (f.as_f64().unwrap().floor() as i64).into(), + (match f.as_f64() { + Some(f) => f.floor() as i64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) + .into(), )), VariableValue::Bool(b) => { if *b { @@ -43,9 +53,9 @@ impl ScalarFunction for ToInteger { Ok(VariableValue::Null) } } - _ => Err(EvaluationError::FunctionError { + _ => Err(FunctionError { function_name: expression.name.to_string(), - error: Box::new(EvaluationError::InvalidType), + error: FunctionEvaluationError::InvalidArgument(0), }), } } @@ -61,17 +71,27 @@ impl ScalarFunction for ToIntegerOrNull { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), VariableValue::Integer(i) => Ok(VariableValue::Integer(i.clone())), VariableValue::Float(f) => Ok(VariableValue::Integer( - (f.as_f64().unwrap().floor() as i64).into(), + (match f.as_f64() { + Some(f) => f.floor() as i64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) + .into(), )), VariableValue::Bool(b) => { if *b { diff --git a/core/src/evaluation/functions/drasi/max.rs b/core/src/evaluation/functions/drasi/max.rs index 25d543b..3fd3a4f 100644 --- a/core/src/evaluation/functions/drasi/max.rs +++ b/core/src/evaluation/functions/drasi/max.rs @@ -1,5 +1,5 @@ use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::EvaluationError; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use async_trait::async_trait; use drasi_query_ast::ast; @@ -13,13 +13,14 @@ impl ScalarFunction for DrasiMax { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.is_empty() || args.len() > 2 { - return Err(EvaluationError::InvalidArgumentCount( - "drasi.listMax".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => { @@ -46,7 +47,10 @@ impl ScalarFunction for DrasiMax { VariableValue::ZonedTime(t) => Ok(VariableValue::ZonedTime(*t)), VariableValue::ZonedDateTime(dt) => Ok(VariableValue::ZonedDateTime(dt.clone())), VariableValue::Duration(d) => Ok(VariableValue::Duration(d.clone())), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/drasi/min.rs b/core/src/evaluation/functions/drasi/min.rs index c7a6db2..85d225a 100644 --- a/core/src/evaluation/functions/drasi/min.rs +++ b/core/src/evaluation/functions/drasi/min.rs @@ -1,5 +1,5 @@ use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::EvaluationError; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use async_trait::async_trait; use drasi_query_ast::ast; @@ -13,13 +13,14 @@ impl ScalarFunction for DrasiMin { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "drasi.listMin".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => { @@ -46,7 +47,10 @@ impl ScalarFunction for DrasiMin { VariableValue::ZonedTime(t) => Ok(VariableValue::ZonedTime(*t)), VariableValue::ZonedDateTime(dt) => Ok(VariableValue::ZonedDateTime(dt.clone())), VariableValue::Duration(d) => Ok(VariableValue::Duration(d.clone())), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/drasi/stdevp.rs b/core/src/evaluation/functions/drasi/stdevp.rs index a2b6af6..8be048e 100644 --- a/core/src/evaluation/functions/drasi/stdevp.rs +++ b/core/src/evaluation/functions/drasi/stdevp.rs @@ -1,5 +1,5 @@ use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::EvaluationError; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use async_trait::async_trait; use drasi_query_ast::ast; use statistical::{mean, population_standard_deviation}; @@ -15,13 +15,14 @@ impl ScalarFunction for DrasiStdevP { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "drasi.stdevp".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), @@ -30,10 +31,26 @@ impl ScalarFunction for DrasiStdevP { for element in l { match element { VariableValue::Integer(i) => { - cleaned_list.push(i.as_i64().unwrap() as f64); + cleaned_list.push(match i.as_i64() { + Some(i) => i as f64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); } VariableValue::Float(f) => { - cleaned_list.push(f.as_f64().unwrap()); + cleaned_list.push(match f.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); } VariableValue::Null => { continue; @@ -48,7 +65,10 @@ impl ScalarFunction for DrasiStdevP { Ok(VariableValue::Float(stdevp.into())) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/future/awaiting.rs b/core/src/evaluation/functions/future/awaiting.rs index 949c966..fdaa6b9 100644 --- a/core/src/evaluation/functions/future/awaiting.rs +++ b/core/src/evaluation/functions/future/awaiting.rs @@ -1,27 +1,27 @@ -use async_trait::async_trait; -use drasi_query_ast::ast; - -use crate::evaluation::{ - functions::ScalarFunction, variable_value::VariableValue, EvaluationError, - ExpressionEvaluationContext, -}; - -pub struct Awaiting {} - -impl Awaiting { - pub fn new() -> Self { - Awaiting {} - } -} - -#[async_trait] -impl ScalarFunction for Awaiting { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - _args: Vec, - ) -> Result { - Ok(VariableValue::Awaiting) - } -} +use async_trait::async_trait; +use drasi_query_ast::ast; + +use crate::evaluation::{ + functions::ScalarFunction, variable_value::VariableValue, ExpressionEvaluationContext, + FunctionError, +}; + +pub struct Awaiting {} + +impl Awaiting { + pub fn new() -> Self { + Awaiting {} + } +} + +#[async_trait] +impl ScalarFunction for Awaiting { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + _expression: &ast::FunctionExpression, + _args: Vec, + ) -> Result { + Ok(VariableValue::Awaiting) + } +} diff --git a/core/src/evaluation/functions/future/future_element.rs b/core/src/evaluation/functions/future/future_element.rs index 001b862..8f39721 100644 --- a/core/src/evaluation/functions/future/future_element.rs +++ b/core/src/evaluation/functions/future/future_element.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use crate::evaluation::context::SideEffects; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::EvaluationError; use crate::evaluation::ExpressionEvaluationContext; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use crate::interface::{FutureQueue, PushType}; use async_trait::async_trait; use chrono::NaiveTime; @@ -27,16 +27,22 @@ impl ScalarFunction for FutureElement { context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let element = match &args[0] { VariableValue::Element(e) => e, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let due_time = match &args[1] { @@ -47,24 +53,45 @@ impl ScalarFunction for FutureElement { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; let group_signature = context.get_input_grouping_hash(); if due_time <= context.get_realtime() { if let SideEffects::Apply = context.get_side_effects() { - self.future_queue + match self + .future_queue .remove(expression.position_in_query, group_signature) - .await?; + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } } return Ok(args[0].clone()); } if let SideEffects::Apply = context.get_side_effects() { - self.future_queue + match self + .future_queue .push( PushType::Always, expression.position_in_query, @@ -73,7 +100,16 @@ impl ScalarFunction for FutureElement { context.get_transaction_time(), due_time, ) - .await?; + .await + { + Ok(_) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } } Ok(VariableValue::Awaiting) diff --git a/core/src/evaluation/functions/future/true_for.rs b/core/src/evaluation/functions/future/true_for.rs index 19fde79..a69c2a4 100644 --- a/core/src/evaluation/functions/future/true_for.rs +++ b/core/src/evaluation/functions/future/true_for.rs @@ -1,167 +1,270 @@ -use std::sync::Arc; -use std::sync::Weak; - -use crate::evaluation::context::SideEffects; -use crate::evaluation::functions::aggregation::ValueAccumulator; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::EvaluationError; -use crate::evaluation::ExpressionEvaluationContext; -use crate::evaluation::ExpressionEvaluator; -use crate::interface::ResultIndex; -use crate::interface::ResultKey; -use crate::interface::ResultOwner; -use crate::interface::{FutureQueue, PushType}; -use async_trait::async_trait; -use chrono::Duration; -use drasi_query_ast::ast; - -pub struct TrueFor { - future_queue: Arc, - result_index: Arc, - expression_evaluator: Weak, -} - -impl TrueFor { - pub fn new( - future_queue: Arc, - result_index: Arc, - expression_evaluator: Weak, - ) -> Self { - Self { - future_queue, - result_index, - expression_evaluator, - } - } -} - -#[async_trait] -impl ScalarFunction for TrueFor { - async fn call( - &self, - context: &ExpressionEvaluationContext, - expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); - } - - let result_owner = ResultOwner::Function(expression.position_in_query); - - let anchor_element = match context.get_anchor_element() { - Some(anchor) => anchor, - None => return Ok(VariableValue::Null), - }; - - let anchor_ref = anchor_element.get_reference().clone(); - - let condition = match &args[0] { - VariableValue::Bool(b) => b, - VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), - }; - - let duration = match &args[1] { - VariableValue::Duration(d) => *d.duration(), - VariableValue::Integer(n) => match n.as_i64() { - Some(ms) => Duration::milliseconds(ms), - None => return Err(EvaluationError::InvalidType), - }, - VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), - }; - - let group_signature = context.get_input_grouping_hash(); - - let expression_evaluator = match self.expression_evaluator.upgrade() { - Some(evaluator) => evaluator, - None => return Err(EvaluationError::InvalidState), - }; - - let result_key = match context.get_output_grouping_key() { - Some(group_expressions) => { - let mut grouping_vals = Vec::new(); - for group_expression in group_expressions { - grouping_vals.push( - expression_evaluator - .evaluate_expression(context, group_expression) - .await?, - ); - } - ResultKey::GroupBy(Arc::new(grouping_vals)) - } - None => ResultKey::InputHash(group_signature), - }; - - if !*condition { - if let SideEffects::Apply = context.get_side_effects() { - self.result_index - .set(result_key.clone(), result_owner, None) - .await?; - - self.future_queue - .remove(expression.position_in_query, group_signature) - .await?; - } - return Ok(VariableValue::Bool(*condition)); - } - - let due_time = match self.result_index.get(&result_key, &result_owner).await? { - Some(ValueAccumulator::TimeMarker { - timestamp: since_timestamp, - }) => { - if let SideEffects::RevertForDelete = context.get_side_effects() { - self.result_index - .set(result_key.clone(), result_owner, None) - .await?; - } - - since_timestamp + duration.num_milliseconds() as u64 - } - None => { - if let SideEffects::Apply = context.get_side_effects() { - self.result_index - .set( - result_key.clone(), - result_owner, - Some(ValueAccumulator::TimeMarker { - timestamp: context.get_transaction_time(), - }), - ) - .await?; - } - - context.get_transaction_time() + duration.num_milliseconds() as u64 - } - _ => return Err(EvaluationError::InvalidType), - }; - - if due_time <= context.get_realtime() { - if let SideEffects::Apply = context.get_side_effects() { - self.future_queue - .remove(expression.position_in_query, group_signature) - .await?; - } - return Ok(VariableValue::Bool(*condition)); - } - - if let SideEffects::Apply = context.get_side_effects() { - self.future_queue - .push( - PushType::IfNotExists, - expression.position_in_query, - group_signature, - &anchor_ref, - context.get_transaction_time(), - due_time, - ) - .await?; - } - - Ok(VariableValue::Awaiting) - } -} +use std::sync::Arc; +use std::sync::Weak; + +use crate::evaluation::context::SideEffects; +use crate::evaluation::functions::aggregation::ValueAccumulator; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::ExpressionEvaluationContext; +use crate::evaluation::ExpressionEvaluator; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; +use crate::interface::ResultIndex; +use crate::interface::ResultKey; +use crate::interface::ResultOwner; +use crate::interface::{FutureQueue, PushType}; +use async_trait::async_trait; +use chrono::Duration; +use drasi_query_ast::ast; + +pub struct TrueFor { + future_queue: Arc, + result_index: Arc, + expression_evaluator: Weak, +} + +impl TrueFor { + pub fn new( + future_queue: Arc, + result_index: Arc, + expression_evaluator: Weak, + ) -> Self { + Self { + future_queue, + result_index, + expression_evaluator, + } + } +} + +#[async_trait] +impl ScalarFunction for TrueFor { + async fn call( + &self, + context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if args.len() != 2 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + + let result_owner = ResultOwner::Function(expression.position_in_query); + + let anchor_element = match context.get_anchor_element() { + Some(anchor) => anchor, + None => return Ok(VariableValue::Null), + }; + + let anchor_ref = anchor_element.get_reference().clone(); + + let condition = match &args[0] { + VariableValue::Bool(b) => b, + VariableValue::Null => return Ok(VariableValue::Null), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }; + + let duration = match &args[1] { + VariableValue::Duration(d) => *d.duration(), + VariableValue::Integer(n) => match n.as_i64() { + Some(ms) => Duration::milliseconds(ms), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + VariableValue::Null => return Ok(VariableValue::Null), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } + }; + + let group_signature = context.get_input_grouping_hash(); + + let expression_evaluator = match self.expression_evaluator.upgrade() { + Some(evaluator) => evaluator, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + }; + + let result_key = match context.get_output_grouping_key() { + Some(group_expressions) => { + let mut grouping_vals = Vec::new(); + for group_expression in group_expressions { + grouping_vals.push( + match expression_evaluator + .evaluate_expression(context, group_expression) + .await + { + Ok(val) => val, + Err(_e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "VariableValue".to_string(), + }, + }) + } + }, + ); + } + ResultKey::GroupBy(Arc::new(grouping_vals)) + } + None => ResultKey::InputHash(group_signature), + }; + + if !*condition { + if let SideEffects::Apply = context.get_side_effects() { + match self + .result_index + .set(result_key.clone(), result_owner, None) + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + + match self + .future_queue + .remove(expression.position_in_query, group_signature) + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + } + return Ok(VariableValue::Bool(*condition)); + } + + let due_time = match self.result_index.get(&result_key, &result_owner).await { + Ok(Some(ValueAccumulator::TimeMarker { + timestamp: since_timestamp, + })) => { + if let SideEffects::RevertForDelete = context.get_side_effects() { + match self + .result_index + .set(result_key.clone(), result_owner, None) + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + } + + since_timestamp + duration.num_milliseconds() as u64 + } + Ok(None) => { + if let SideEffects::Apply = context.get_side_effects() { + match self + .result_index + .set( + result_key.clone(), + result_owner, + Some(ValueAccumulator::TimeMarker { + timestamp: context.get_transaction_time(), + }), + ) + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + } + + context.get_transaction_time() + duration.num_milliseconds() as u64 + } + Ok(_) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::CorruptData, + }) + } + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; + + if due_time <= context.get_realtime() { + if let SideEffects::Apply = context.get_side_effects() { + match self + .future_queue + .remove(expression.position_in_query, group_signature) + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + } + return Ok(VariableValue::Bool(*condition)); + } + + if let SideEffects::Apply = context.get_side_effects() { + match self + .future_queue + .push( + PushType::IfNotExists, + expression.position_in_query, + group_signature, + &anchor_ref, + context.get_transaction_time(), + due_time, + ) + .await + { + Ok(_) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } + } + + Ok(VariableValue::Awaiting) + } +} diff --git a/core/src/evaluation/functions/future/true_later.rs b/core/src/evaluation/functions/future/true_later.rs index 1dc5cf6..569338c 100644 --- a/core/src/evaluation/functions/future/true_later.rs +++ b/core/src/evaluation/functions/future/true_later.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use crate::evaluation::context::SideEffects; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::EvaluationError; use crate::evaluation::ExpressionEvaluationContext; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use crate::interface::{FutureQueue, PushType}; use async_trait::async_trait; use chrono::NaiveTime; @@ -27,11 +27,12 @@ impl ScalarFunction for TrueLater { context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } println!("TrueLater called:"); @@ -46,7 +47,12 @@ impl ScalarFunction for TrueLater { let condition = match &args[0] { VariableValue::Bool(b) => b, VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; println!("Condition: {}", condition); @@ -59,10 +65,20 @@ impl ScalarFunction for TrueLater { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; println!("Due time: {}", due_time); @@ -78,7 +94,8 @@ impl ScalarFunction for TrueLater { if let SideEffects::Apply = context.get_side_effects() { println!("Adding to future queue"); - self.future_queue + match self + .future_queue .push( PushType::Overwrite, expression.position_in_query, @@ -87,7 +104,16 @@ impl ScalarFunction for TrueLater { context.get_transaction_time(), due_time, ) - .await?; + .await + { + Ok(_) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + } } println!("Returning Awaiting"); diff --git a/core/src/evaluation/functions/future/true_until.rs b/core/src/evaluation/functions/future/true_until.rs index 51807f5..45d0164 100644 --- a/core/src/evaluation/functions/future/true_until.rs +++ b/core/src/evaluation/functions/future/true_until.rs @@ -3,8 +3,8 @@ use std::sync::Arc; use crate::evaluation::context::SideEffects; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::EvaluationError; use crate::evaluation::ExpressionEvaluationContext; +use crate::evaluation::{FunctionError, FunctionEvaluationError}; use crate::interface::{FutureQueue, PushType}; use async_trait::async_trait; use chrono::NaiveTime; @@ -27,11 +27,12 @@ impl ScalarFunction for TrueUntil { context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let anchor_ref = match context.get_anchor_element() { Some(anchor) => anchor.get_reference().clone(), @@ -41,7 +42,12 @@ impl ScalarFunction for TrueUntil { let condition = match &args[0] { VariableValue::Bool(b) => b, VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let due_time = match &args[1] { @@ -52,34 +58,65 @@ impl ScalarFunction for TrueUntil { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; let group_signature = context.get_input_grouping_hash(); if !*condition { if let SideEffects::Apply = context.get_side_effects() { - self.future_queue + match self + .future_queue .remove(expression.position_in_query, group_signature) - .await?; + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; } return Ok(VariableValue::Bool(*condition)); } if due_time <= context.get_realtime() { if let SideEffects::Apply = context.get_side_effects() { - self.future_queue + match self + .future_queue .remove(expression.position_in_query, group_signature) - .await?; + .await + { + Ok(()) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; } return Ok(VariableValue::Bool(*condition)); } if let SideEffects::Apply = context.get_side_effects() { - self.future_queue + match self + .future_queue .push( PushType::IfNotExists, expression.position_in_query, @@ -88,7 +125,16 @@ impl ScalarFunction for TrueUntil { context.get_transaction_time(), due_time, ) - .await?; + .await + { + Ok(_) => (), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; } Ok(VariableValue::Awaiting) diff --git a/core/src/evaluation/functions/list/range.rs b/core/src/evaluation/functions/list/range.rs index 037d9b8..da4c6d6 100644 --- a/core/src/evaluation/functions/list/range.rs +++ b/core/src/evaluation/functions/list/range.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Range {} @@ -15,24 +15,48 @@ impl ScalarFunction for Range { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() < 2 || args.len() > 3 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match args.len() { 2 => match (&args[0], &args[1]) { (VariableValue::Integer(start), VariableValue::Integer(end)) => { let mut range = Vec::new(); - let start = start.as_i64().unwrap(); - let end = end.as_i64().unwrap(); + let start = match start.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + let end = match end.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; for i in start..end + 1 { range.push(VariableValue::Integer(i.into())); } Ok(VariableValue::List(range)) } - _ => Err(EvaluationError::InvalidType), + (VariableValue::Integer(_), _) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), }, 3 => match (&args[0], &args[1], &args[2]) { ( @@ -41,15 +65,54 @@ impl ScalarFunction for Range { VariableValue::Integer(step), ) => { let mut range = Vec::new(); - let start = start.as_i64().unwrap(); - let end = end.as_i64().unwrap(); - let step = step.as_i64().unwrap(); + let start = match start.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + let end = match end.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + let step = match step.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; for i in (start..end + 1).step_by(step as usize) { range.push(VariableValue::Integer(i.into())); } Ok(VariableValue::List(range)) } - _ => Err(EvaluationError::InvalidType), + (VariableValue::Integer(_), VariableValue::Integer(_), _) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }), + (_, VariableValue::Integer(_), VariableValue::Integer(_)) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + (VariableValue::Integer(_), _, VariableValue::Integer(_)) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), }, _ => unreachable!(), } diff --git a/core/src/evaluation/functions/list/reduce.rs b/core/src/evaluation/functions/list/reduce.rs index a9568f0..fc8c3e7 100644 --- a/core/src/evaluation/functions/list/reduce.rs +++ b/core/src/evaluation/functions/list/reduce.rs @@ -7,7 +7,9 @@ use std::sync::Arc; use crate::evaluation::functions::LazyScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, ExpressionEvaluator}; +use crate::evaluation::{ + ExpressionEvaluationContext, ExpressionEvaluator, FunctionError, FunctionEvaluationError, +}; use drasi_query_ast::ast::{self, Expression}; pub struct Reduce { @@ -31,32 +33,60 @@ impl LazyScalarFunction for Reduce { async fn call( &self, context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: &Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("reduce".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let initializer = &args[0]; let iterator = match &args[1] { Expression::IteratorExpression(i) => i, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; - let (accumulator_name, accumulator) = self + let (accumulator_name, accumulator) = match self .evaluator .evaluate_assignment(context, initializer) - .await?; + .await + { + Ok((name, value)) => (name, value), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } + }; let mut query_variables = context.clone_variables(); //Retrieve the query variables from the global context query_variables.insert(accumulator_name.to_string().into(), accumulator); let context = ExpressionEvaluationContext::new(&query_variables, context.get_clock()); - let result = self + let result = match self .evaluator .reduce_iterator_expression(&context, iterator, accumulator_name) - .await?; + .await + { + Ok(value) => value, + Err(_e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "Valid reduce expression".to_string(), + }, + }) + } + }; Ok(result) } diff --git a/core/src/evaluation/functions/list/tail.rs b/core/src/evaluation/functions/list/tail.rs index 3b61661..a6de096 100644 --- a/core/src/evaluation/functions/list/tail.rs +++ b/core/src/evaluation/functions/list/tail.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Tail {} @@ -15,11 +15,12 @@ impl ScalarFunction for Tail { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::List(l) => { @@ -29,7 +30,10 @@ impl ScalarFunction for Tail { Ok(VariableValue::List(l[1..].to_vec())) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/metadata.rs b/core/src/evaluation/functions/metadata.rs index a598528..012b21b 100644 --- a/core/src/evaluation/functions/metadata.rs +++ b/core/src/evaluation/functions/metadata.rs @@ -1,73 +1,81 @@ -use std::sync::Arc; - -use crate::evaluation::variable_value::{zoned_datetime::ZonedDateTime, VariableValue}; -use async_trait::async_trait; -use drasi_query_ast::ast; - -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; - -use super::{Function, FunctionRegistry, ScalarFunction}; - -pub trait RegisterMetadataFunctions { - fn register_metadata_functions(&self); -} - -impl RegisterMetadataFunctions for FunctionRegistry { - fn register_metadata_functions(&self) { - self.register_function("elementId", Function::Scalar(Arc::new(ElementId {}))); - self.register_function( - "drasi.changeDateTime", - Function::Scalar(Arc::new(ChangeDateTime {})), - ); - } -} - -#[derive(Debug)] -pub struct ElementId {} - -#[async_trait] -impl ScalarFunction for ElementId { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "elementId".to_string(), - )); - } - match &args[0] { - VariableValue::Element(e) => Ok(VariableValue::String( - e.get_reference().element_id.to_string(), - )), - _ => Err(EvaluationError::InvalidType), - } - } -} - -#[derive(Debug)] -pub struct ChangeDateTime {} - -#[async_trait] -impl ScalarFunction for ChangeDateTime { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - expression.name.to_string(), - )); - } - match &args[0] { - VariableValue::Element(e) => Ok(VariableValue::ZonedDateTime( - ZonedDateTime::from_epoch_millis(e.get_effective_from()), - )), - _ => Err(EvaluationError::InvalidType), - } - } -} +use std::sync::Arc; + +use crate::evaluation::variable_value::{zoned_datetime::ZonedDateTime, VariableValue}; +use async_trait::async_trait; +use drasi_query_ast::ast; + +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; + +use super::{Function, FunctionRegistry, ScalarFunction}; + +pub trait RegisterMetadataFunctions { + fn register_metadata_functions(&self); +} + +impl RegisterMetadataFunctions for FunctionRegistry { + fn register_metadata_functions(&self) { + self.register_function("elementId", Function::Scalar(Arc::new(ElementId {}))); + self.register_function( + "drasi.changeDateTime", + Function::Scalar(Arc::new(ChangeDateTime {})), + ); + } +} + +#[derive(Debug)] +pub struct ElementId {} + +#[async_trait] +impl ScalarFunction for ElementId { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + match &args[0] { + VariableValue::Element(e) => Ok(VariableValue::String( + e.get_reference().element_id.to_string(), + )), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + } + } +} + +#[derive(Debug)] +pub struct ChangeDateTime {} + +#[async_trait] +impl ScalarFunction for ChangeDateTime { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + match &args[0] { + VariableValue::Element(e) => Ok(VariableValue::ZonedDateTime( + ZonedDateTime::from_epoch_millis(e.get_effective_from()), + )), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + } + } +} diff --git a/core/src/evaluation/functions/mod.rs b/core/src/evaluation/functions/mod.rs index 48062e9..15c22a9 100644 --- a/core/src/evaluation/functions/mod.rs +++ b/core/src/evaluation/functions/mod.rs @@ -25,7 +25,7 @@ use self::{ text::RegisterTextFunctions, }; -use super::{EvaluationError, ExpressionEvaluationContext, ExpressionEvaluator}; +use super::{ExpressionEvaluationContext, FunctionError}; pub mod aggregation; pub mod context_mutators; @@ -54,7 +54,7 @@ pub trait ScalarFunction: Send + Sync { context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result; + ) -> Result; } #[async_trait] @@ -64,7 +64,7 @@ pub trait LazyScalarFunction: Send + Sync { context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: &Vec, - ) -> Result; + ) -> Result; } #[async_trait] @@ -81,19 +81,19 @@ pub trait AggregatingFunction: Debug + Send + Sync { context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result; + ) -> Result; async fn revert( &self, context: &ExpressionEvaluationContext, args: Vec, accumulator: &mut Accumulator, - ) -> Result; + ) -> Result; async fn snapshot( &self, context: &ExpressionEvaluationContext, args: Vec, accumulator: &Accumulator, - ) -> Result; + ) -> Result; fn accumulator_is_lazy(&self) -> bool; } @@ -103,7 +103,7 @@ pub trait ContextMutatorFunction: Send + Sync { &self, context: &ExpressionEvaluationContext<'a>, expression: &ast::FunctionExpression, - ) -> Result, EvaluationError>; + ) -> Result, FunctionError>; } pub struct FunctionRegistry { diff --git a/core/src/evaluation/functions/numeric/abs.rs b/core/src/evaluation/functions/numeric/abs.rs index 3e6f78a..f24a484 100644 --- a/core/src/evaluation/functions/numeric/abs.rs +++ b/core/src/evaluation/functions/numeric/abs.rs @@ -1,34 +1,62 @@ -use async_trait::async_trait; -use drasi_query_ast::ast; - -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; - -#[derive(Debug)] -pub struct Abs {} - -#[async_trait] -impl ScalarFunction for Abs { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("abs".to_string())); - } - match &args[0] { - VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(n) => { - Ok(VariableValue::Integer(n.as_i64().unwrap().abs().into())) - } - VariableValue::Float(n) => Ok(VariableValue::Float( - Float::from_f64(n.as_f64().unwrap().abs()).unwrap(), - )), - _ => Err(EvaluationError::InvalidType), - } - } -} +use async_trait::async_trait; +use drasi_query_ast::ast; + +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; + +#[derive(Debug)] +pub struct Abs {} + +#[async_trait] +impl ScalarFunction for Abs { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + match &args[0] { + VariableValue::Null => Ok(VariableValue::Null), + VariableValue::Integer(n) => Ok(VariableValue::Integer(match n.as_i64() { + Some(i) => i.abs().into(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })), + VariableValue::Float(n) => Ok(VariableValue::Float( + match Float::from_f64(match n.as_f64() { + Some(f) => f.abs(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + )), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + } + } +} diff --git a/core/src/evaluation/functions/numeric/ceil.rs b/core/src/evaluation/functions/numeric/ceil.rs index 7a94164..198b006 100644 --- a/core/src/evaluation/functions/numeric/ceil.rs +++ b/core/src/evaluation/functions/numeric/ceil.rs @@ -4,7 +4,7 @@ use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::float::Float; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Ceil {} @@ -14,23 +14,59 @@ impl ScalarFunction for Ceil { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("ceil".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(n) => { - Ok(VariableValue::Float( - Float::from_f64(n.as_i64().unwrap() as f64).unwrap(), - )) // ceil always return a float - } + VariableValue::Integer(n) => Ok(VariableValue::Float( + match Float::from_f64(match n.as_i64() { + Some(n) => n as f64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + )), VariableValue::Float(n) => Ok(VariableValue::Float( - Float::from_f64(n.as_f64().unwrap().ceil()).unwrap(), + match Float::from_f64(match n.as_f64() { + Some(n) => n.ceil(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(n) => n, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/numeric/floor.rs b/core/src/evaluation/functions/numeric/floor.rs index da0d9f9..d9e37b1 100644 --- a/core/src/evaluation/functions/numeric/floor.rs +++ b/core/src/evaluation/functions/numeric/floor.rs @@ -3,9 +3,8 @@ use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::integer::Integer; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug)] pub struct Floor {} @@ -15,23 +14,61 @@ impl ScalarFunction for Floor { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("floor".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => Ok(VariableValue::Null), VariableValue::Integer(n) => { Ok(VariableValue::Float( - Float::from_f64(n.as_i64().unwrap() as f64).unwrap(), + match Float::from_f64(match n.as_i64() { + Some(i) => i as f64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )) // floor always return a float } VariableValue::Float(n) => Ok(VariableValue::Float( - Float::from_f64(n.as_f64().unwrap().floor()).unwrap(), + match Float::from_f64(match n.as_f64() { + Some(f) => f.floor(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } diff --git a/core/src/evaluation/functions/numeric/numeric_round.rs b/core/src/evaluation/functions/numeric/numeric_round.rs index 92fa72c..2962e1d 100644 --- a/core/src/evaluation/functions/numeric/numeric_round.rs +++ b/core/src/evaluation/functions/numeric/numeric_round.rs @@ -5,7 +5,7 @@ use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::float::Float; use crate::evaluation::variable_value::integer::Integer; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; use std::collections::HashSet; extern crate round; @@ -19,11 +19,14 @@ impl ScalarFunction for Round { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.is_empty() || args.len() > 3 { - return Err(EvaluationError::InvalidArgumentCount("round".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.contains(&VariableValue::Null) { return Ok(VariableValue::Null); @@ -33,51 +36,176 @@ impl ScalarFunction for Round { match &args[0] { VariableValue::Null => Ok(VariableValue::Null), VariableValue::Integer(n) => Ok(VariableValue::Float( - Float::from_f64(n.as_i64().unwrap() as f64).unwrap(), + match Float::from_f64(match n.as_i64() { + Some(i) => i as f64, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )), VariableValue::Float(n) => { - let input_as_f64 = n.as_f64().unwrap(); + let input_as_f64 = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; if input_as_f64.fract() == -0.5 { //Cypher edge case return Ok(VariableValue::Float( - Float::from_f64(input_as_f64.trunc()).unwrap(), + match Float::from_f64(input_as_f64.trunc()) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } Ok(VariableValue::Float( - Float::from_f64(n.as_f64().unwrap().round()).unwrap(), + match Float::from_f64(match n.as_f64() { + Some(f) => f.round(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } 2 => { match (&args[0], &args[1]) { (VariableValue::Float(n), VariableValue::Integer(p)) => { - let multiplier = 10.0_f64.powi(p.as_i64().unwrap() as i32); + let multiplier = 10.0_f64.powi(match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); //edge case - if n.as_f64().unwrap() > std::f64::MAX / multiplier { + if match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } > std::f64::MAX / multiplier + { return Ok(VariableValue::Float(n.clone())); } - let intermediate_value = n.as_f64().unwrap() * multiplier; + let intermediate_value = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier; if intermediate_value.is_sign_negative() && intermediate_value.fract() == -0.5 { //Cypher edge case let rounded_value = intermediate_value.trunc() / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } - let rounded_value = (n.as_f64().unwrap() * multiplier).round() / multiplier; - Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), - )) + let rounded_value = (match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier) + .round() + / multiplier; + Ok(VariableValue::Float(match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) } (VariableValue::Integer(n), VariableValue::Integer(_p)) => { - Ok(VariableValue::Integer(Integer::from(n.as_i64().unwrap()))) + Ok(VariableValue::Integer(Integer::from(match n.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }))) + } + (VariableValue::Float(_n), _) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }); + } + (VariableValue::Integer(_n), _) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }); + } + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } - _ => Err(EvaluationError::InvalidType), } } 3 => { @@ -102,117 +230,444 @@ impl ScalarFunction for Round { // let valid_keys: HashSet = vec!["year", "month", "week", "day", "ordinalDay", "quarter", "dayOfWeek", "dayOfQuarter"].iter().map(|s| s.to_string()).collect(); let mode = m.to_uppercase(); if !valid_modes.contains(&mode) { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }); } - let is_positive = n.as_f64().unwrap().is_sign_positive(); + let is_positive = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } + .is_sign_positive(); match mode.as_str() { "UP" => { if is_positive { - let result = - round_up(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); + let result = round_up( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); return Ok(VariableValue::Float( - Float::from_f64(result).unwrap(), + match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } else { //Cypher being weird :) - let result = - round_down(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); + let result = round_down( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); return Ok(VariableValue::Float( - Float::from_f64(result).unwrap(), + match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } } "DOWN" => { if is_positive { - let result = - round_down(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); + let result = round_down( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); return Ok(VariableValue::Float( - Float::from_f64(result).unwrap(), + match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } else { - let result = - round_up(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); + let result = round_up( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); return Ok(VariableValue::Float( - Float::from_f64(result).unwrap(), + match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } } "CEILING" => { - let result = - round_up(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); - return Ok(VariableValue::Float(Float::from_f64(result).unwrap())); + let result = round_up( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); + return Ok(VariableValue::Float(match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })); } "FLOOR" => { - let result = - round_down(n.as_f64().unwrap(), p.as_i64().unwrap() as i32); - return Ok(VariableValue::Float(Float::from_f64(result).unwrap())); + let result = round_down( + match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, + ); + return Ok(VariableValue::Float(match Float::from_f64(result) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })); } "HALF_UP" => { - let multiplier = 10.0_f64.powi(p.as_i64().unwrap() as i32); - if n.as_f64().unwrap() > std::f64::MAX / multiplier { + let multiplier = 10.0_f64.powi(match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); + if match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } > std::f64::MAX / multiplier + { return Ok(VariableValue::Float(n.clone())); } - let intermediate_value = n.as_f64().unwrap() * multiplier; + let intermediate_value = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier; if intermediate_value.fract() == 0.5 { let rounded_value = (intermediate_value.trunc() + 1.0) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } else if intermediate_value.fract() == -0.5 { let rounded_value = (intermediate_value.trunc() - 1.0) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } - let rounded_value = - (n.as_f64().unwrap() * multiplier).round() / multiplier; + let rounded_value = (match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier) + .round() + / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } "HALF_DOWN" => { - let multiplier = 10.0_f64.powi(p.as_i64().unwrap() as i32); - if n.as_f64().unwrap() > std::f64::MAX / multiplier { + let multiplier = 10.0_f64.powi(match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); + if match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } > std::f64::MAX / multiplier + { return Ok(VariableValue::Float(n.clone())); } - let intermediate_value = n.as_f64().unwrap() * multiplier; + let intermediate_value = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier; if intermediate_value.fract() == 0.5 { let rounded_value = (intermediate_value.trunc()) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } else if intermediate_value.fract() == -0.5 { let rounded_value = (intermediate_value.trunc()) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } - let rounded_value = - (n.as_f64().unwrap() * multiplier).round() / multiplier; + let rounded_value = (match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier) + .round() + / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } "HALF_EVEN" => { - let multiplier = 10.0_f64.powi(p.as_i64().unwrap() as i32); - if n.as_f64().unwrap() > std::f64::MAX / multiplier { + let multiplier = 10.0_f64.powi(match p.as_i64() { + Some(i) => i as i32, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }); + if match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } > std::f64::MAX / multiplier + { return Ok(VariableValue::Float(n.clone())); } - let intermediate_value = n.as_f64().unwrap() * multiplier; + let intermediate_value = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier; if intermediate_value.fract() == 0.5 { if intermediate_value.trunc() % 2.0 == 0.0 { let rounded_value = (intermediate_value.trunc()) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: + FunctionEvaluationError::OverflowError, + }) + } + }, )); } else { let rounded_value = (intermediate_value.trunc() + 1.0) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: + FunctionEvaluationError::OverflowError, + }) + } + }, )); } } else if intermediate_value.fract() == -0.5 { @@ -220,34 +675,85 @@ impl ScalarFunction for Round { let rounded_value = (intermediate_value.trunc()) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: + FunctionEvaluationError::OverflowError, + }) + } + }, )); } else { let rounded_value = (intermediate_value.trunc() - 1.0) / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: + FunctionEvaluationError::OverflowError, + }) + } + }, )); } } - let rounded_value = - (n.as_f64().unwrap() * multiplier).round() / multiplier; + let rounded_value = (match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + } * multiplier) + .round() + / multiplier; return Ok(VariableValue::Float( - Float::from_f64(rounded_value).unwrap(), + match Float::from_f64(rounded_value) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, )); } - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }) + } } } ( VariableValue::Integer(n), VariableValue::Integer(_p), VariableValue::Integer(_m), - ) => Ok(VariableValue::Integer(Integer::from(n.as_i64().unwrap()))), - _ => Err(EvaluationError::InvalidType), + ) => Ok(VariableValue::Integer(Integer::from(match n.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }))), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }), } } - _ => Err(EvaluationError::InvalidArgumentCount("round".to_string())), + _ => unreachable!(), } } } diff --git a/core/src/evaluation/functions/numeric/random.rs b/core/src/evaluation/functions/numeric/random.rs index e2ab8c5..2baf58f 100644 --- a/core/src/evaluation/functions/numeric/random.rs +++ b/core/src/evaluation/functions/numeric/random.rs @@ -1,27 +1,38 @@ -use async_trait::async_trait; -use drasi_query_ast::ast; -use rand::prelude::*; - -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; - -#[derive(Debug)] -pub struct Rand {} - -#[async_trait] -impl ScalarFunction for Rand { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if !args.is_empty() { - return Err(EvaluationError::InvalidArgumentCount("rand".to_string())); - } - let mut rng = rand::thread_rng(); - Ok(VariableValue::Float(Float::from_f64(rng.gen()).unwrap())) - } -} +use async_trait::async_trait; +use drasi_query_ast::ast; +use rand::prelude::*; + +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; + +#[derive(Debug)] +pub struct Rand {} + +#[async_trait] +impl ScalarFunction for Rand { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if !args.is_empty() { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + let mut rng = rand::thread_rng(); + Ok(VariableValue::Float(match Float::from_f64(rng.gen()) { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + })) + } +} diff --git a/core/src/evaluation/functions/numeric/sign.rs b/core/src/evaluation/functions/numeric/sign.rs index a2db5a4..3e8cbc5 100644 --- a/core/src/evaluation/functions/numeric/sign.rs +++ b/core/src/evaluation/functions/numeric/sign.rs @@ -1,47 +1,69 @@ -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; -use async_trait::async_trait; -use drasi_query_ast::ast; - -#[derive(Debug)] -pub struct Sign {} - -#[async_trait] -impl ScalarFunction for Sign { - async fn call( - &self, - _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, - args: Vec, - ) -> Result { - if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("sign".to_string())); - } - match &args[0] { - VariableValue::Null => Ok(VariableValue::Null), - VariableValue::Integer(n) => { - let f = n.as_i64().unwrap(); - if f > 0 { - Ok(VariableValue::Integer(Integer::from(1))) - } else if f < 0 { - Ok(VariableValue::Integer(Integer::from(-1))) - } else { - Ok(VariableValue::Integer(Integer::from(0))) - } - } - VariableValue::Float(n) => { - let f = n.as_f64().unwrap(); - if f > 0.0 { - Ok(VariableValue::Integer(Integer::from(1))) - } else if f < 0.0 { - Ok(VariableValue::Integer(Integer::from(-1))) - } else { - Ok(VariableValue::Integer(Integer::from(0))) - } - } - _ => Err(EvaluationError::InvalidType), - } - } -} +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; +use async_trait::async_trait; +use drasi_query_ast::ast; + +#[derive(Debug)] +pub struct Sign {} + +#[async_trait] +impl ScalarFunction for Sign { + async fn call( + &self, + _context: &ExpressionEvaluationContext, + expression: &ast::FunctionExpression, + args: Vec, + ) -> Result { + if args.len() != 1 { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); + } + match &args[0] { + VariableValue::Null => Ok(VariableValue::Null), + VariableValue::Integer(n) => { + let f = match n.as_i64() { + Some(i) => i, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + if f > 0 { + Ok(VariableValue::Integer(Integer::from(1))) + } else if f < 0 { + Ok(VariableValue::Integer(Integer::from(-1))) + } else { + Ok(VariableValue::Integer(Integer::from(0))) + } + } + VariableValue::Float(n) => { + let f = match n.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }; + if f > 0.0 { + Ok(VariableValue::Integer(Integer::from(1))) + } else if f < 0.0 { + Ok(VariableValue::Integer(Integer::from(-1))) + } else { + Ok(VariableValue::Integer(Integer::from(0))) + } + } + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + } + } +} diff --git a/core/src/evaluation/functions/numeric/tests/abs_tests.rs b/core/src/evaluation/functions/numeric/tests/abs_tests.rs index fa23517..fa1a3a3 100644 --- a/core/src/evaluation/functions/numeric/tests/abs_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/abs_tests.rs @@ -1,200 +1,208 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Abs; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -#[tokio::test] -async fn abs_too_few_args() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn abs_too_many_args() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(0.into()), - VariableValue::Integer(0.into()), - ]; - - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn abs_null() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Null]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, VariableValue::Null); -} - -#[tokio::test] -async fn abs_zero_int() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(0.into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 0); -} - -#[tokio::test] -async fn abs_negative_int() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer((-1).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 1); -} - -#[tokio::test] -async fn abs_positive_int() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(1.into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 1); -} - -#[tokio::test] -async fn abs_zero_float() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float((0_f64).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn abs_negative_float() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float((-1.001).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 1.001_f64); -} - -#[tokio::test] -async fn abs_positive_float() { - let abs = Abs {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float((1.001).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("abs"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 1.001_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Abs; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +#[tokio::test] +async fn abs_too_few_args() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn abs_too_many_args() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(0.into()), + VariableValue::Integer(0.into()), + ]; + + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn abs_null() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Null]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, VariableValue::Null); +} + +#[tokio::test] +async fn abs_zero_int() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(0.into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 0); +} + +#[tokio::test] +async fn abs_negative_int() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer((-1).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 1); +} + +#[tokio::test] +async fn abs_positive_int() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(1.into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 1); +} + +#[tokio::test] +async fn abs_zero_float() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float((0_f64).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn abs_negative_float() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float((-1.001).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 1.001_f64); +} + +#[tokio::test] +async fn abs_positive_float() { + let abs = Abs {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float((1.001).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("abs"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = abs.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 1.001_f64); +} diff --git a/core/src/evaluation/functions/numeric/tests/ceil_tests.rs b/core/src/evaluation/functions/numeric/tests/ceil_tests.rs index 8a9f368..7799d99 100644 --- a/core/src/evaluation/functions/numeric/tests/ceil_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/ceil_tests.rs @@ -1,180 +1,188 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -// use serde_json::{Value, Number}; -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Ceil; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -#[tokio::test] -async fn ceil_too_few_args() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn ceil_too_many_args() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(0.into()), - VariableValue::Integer(0.into()), - ]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn ceil_null() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Null]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, VariableValue::Null); -} - -#[tokio::test] -async fn ceil_zero_float() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float((0_f64).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn ceil_negative_int() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn ceil_negative_float() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-1.001).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn ceil_positive_int() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1_f64).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 1_f64); -} - -#[tokio::test] -async fn ceil_positive_float() { - let ceil = Ceil {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("ceil"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Integer(0)), - )], - position_in_query: 10, - }; - - let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); - assert_eq!(result, 2_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +// use serde_json::{Value, Number}; +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Ceil; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +#[tokio::test] +async fn ceil_too_few_args() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn ceil_too_many_args() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(0.into()), + VariableValue::Integer(0.into()), + ]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn ceil_null() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Null]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, VariableValue::Null); +} + +#[tokio::test] +async fn ceil_zero_float() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float((0_f64).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn ceil_negative_int() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn ceil_negative_float() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-1.001).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn ceil_positive_int() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1_f64).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 1_f64); +} + +#[tokio::test] +async fn ceil_positive_float() { + let ceil = Ceil {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("ceil"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Integer(0)), + )], + position_in_query: 10, + }; + + let result = ceil.call(&context, &func_expr, args.clone()).await.unwrap(); + assert_eq!(result, 2_f64); +} diff --git a/core/src/evaluation/functions/numeric/tests/floor_tests.rs b/core/src/evaluation/functions/numeric/tests/floor_tests.rs index 1444c68..7aa5887 100644 --- a/core/src/evaluation/functions/numeric/tests/floor_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/floor_tests.rs @@ -1,200 +1,208 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Floor; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -#[tokio::test] -async fn floor_too_few_args() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Real(0.0)), - )], - position_in_query: 10, - }; - - let result = floor.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn floor_too_many_args() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(0)), - ]; - - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Real(0.0)), - )], - position_in_query: 10, - }; - - let result = floor.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn floor_null() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Null]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - assert_eq!(result, VariableValue::Null); -} - -#[tokio::test] -async fn floor_zero_float() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn floor_negative_int() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn floor_negative_float() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-0.001).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn floor_positive_int() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float((1_f64).into())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - - assert_eq!(result, 1_f64); -} - -#[tokio::test] -async fn floor_positive_float() { - let floor = Floor {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; - let func_expr = ast::FunctionExpression { - name: Arc::from("floor"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = floor - .call(&context, &func_expr, args.clone()) - .await - .unwrap(); - assert_eq!(result, 1_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Floor; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +#[tokio::test] +async fn floor_too_few_args() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Real(0.0)), + )], + position_in_query: 10, + }; + + let result = floor.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn floor_too_many_args() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(0)), + ]; + + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Real(0.0)), + )], + position_in_query: 10, + }; + + let result = floor.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn floor_null() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Null]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + assert_eq!(result, VariableValue::Null); +} + +#[tokio::test] +async fn floor_zero_float() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn floor_negative_int() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn floor_negative_float() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-0.001).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn floor_positive_int() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float((1_f64).into())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + + assert_eq!(result, 1_f64); +} + +#[tokio::test] +async fn floor_positive_float() { + let floor = Floor {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; + let func_expr = ast::FunctionExpression { + name: Arc::from("floor"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = floor + .call(&context, &func_expr, args.clone()) + .await + .unwrap(); + assert_eq!(result, 1_f64); +} diff --git a/core/src/evaluation/functions/numeric/tests/random_tests.rs b/core/src/evaluation/functions/numeric/tests/random_tests.rs index a91d65f..66c670a 100644 --- a/core/src/evaluation/functions/numeric/tests/random_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/random_tests.rs @@ -1,70 +1,75 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Rand; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -#[tokio::test] -async fn rand_too_many_args() { - let rand = Rand {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(Integer::from(0))]; - let func_expr = ast::FunctionExpression { - name: Arc::from("rand"), - args: vec![ast::Expression::UnaryExpression( - ast::UnaryExpression::Literal(ast::Literal::Null), - )], - position_in_query: 10, - }; - - let result = rand.call(&context, &func_expr, args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn rand_is_f64() { - let rand = Rand {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - let func_expr = ast::FunctionExpression { - name: Arc::from("rand"), - args: vec![], - position_in_query: 10, - }; - - let result = rand.call(&context, &func_expr, args.clone()).await.unwrap(); - assert!(result.is_f64()); -} - -#[tokio::test] -async fn rand_in_range() { - let rand = Rand {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - let func_expr = ast::FunctionExpression { - name: Arc::from("rand"), - args: vec![], - position_in_query: 10, - }; - - let result = rand.call(&context, &func_expr, args.clone()).await.unwrap(); - assert!(result.as_f64().unwrap() >= 0_f64); - assert!(result.as_f64().unwrap() <= 1_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Rand; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +#[tokio::test] +async fn rand_too_many_args() { + let rand = Rand {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(Integer::from(0))]; + let func_expr = ast::FunctionExpression { + name: Arc::from("rand"), + args: vec![ast::Expression::UnaryExpression( + ast::UnaryExpression::Literal(ast::Literal::Null), + )], + position_in_query: 10, + }; + + let result = rand.call(&context, &func_expr, args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn rand_is_f64() { + let rand = Rand {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + let func_expr = ast::FunctionExpression { + name: Arc::from("rand"), + args: vec![], + position_in_query: 10, + }; + + let result = rand.call(&context, &func_expr, args.clone()).await.unwrap(); + assert!(result.is_f64()); +} + +#[tokio::test] +async fn rand_in_range() { + let rand = Rand {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + let func_expr = ast::FunctionExpression { + name: Arc::from("rand"), + args: vec![], + position_in_query: 10, + }; + + let result = rand.call(&context, &func_expr, args.clone()).await.unwrap(); + assert!(result.as_f64().unwrap() >= 0_f64); + assert!(result.as_f64().unwrap() <= 1_f64); +} diff --git a/core/src/evaluation/functions/numeric/tests/round_tests.rs b/core/src/evaluation/functions/numeric/tests/round_tests.rs index 4ca4e46..a9f9f68 100644 --- a/core/src/evaluation/functions/numeric/tests/round_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/round_tests.rs @@ -1,629 +1,637 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Round; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -fn get_func_expr() -> ast::FunctionExpression { - ast::FunctionExpression { - name: Arc::from("round"), - args: vec![], - position_in_query: 10, - } -} - -#[tokio::test] -async fn round_too_few_args() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - - let result = round.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn round_too_many_args() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(0)), - ]; - - let result = round.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn round_null() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Null]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, VariableValue::Null); -} - -#[tokio::test] -async fn round_zero_float() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn round_negative_int() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn round_negative_float_up() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-0.001).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn round_negative_float_down() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-0.7).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1_f64); -} - -#[tokio::test] -async fn round_positive_int() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1_f64).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1_f64); -} - -#[tokio::test] -async fn round_positive_float_up() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1.6).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 2_f64); -} - -#[tokio::test] -async fn round_positive_float_down() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1_f64); -} - -#[tokio::test] -async fn round_positive_float_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(3.1415926).unwrap()), - VariableValue::Integer(Integer::from(3)), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 3.142_f64); -} - -#[tokio::test] -async fn round_negative_float_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.5).unwrap()), - VariableValue::Integer(Integer::from(0)), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.0_f64); -} - -#[tokio::test] -async fn round_negative_float_ties_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.5555).unwrap()), - VariableValue::Integer(Integer::from(3)), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.555_f64); -} - -#[tokio::test] -async fn round_positive_float_up_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.3_f64); -} - -#[tokio::test] -async fn round_negative_float_up_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.35).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.4_f64); -} - -#[tokio::test] -async fn round_positive_float_down_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); -} - -#[tokio::test] -async fn round_negative_float_down_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.2_f64); -} - -#[tokio::test] -async fn round_positive_float_ceiling_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("CEILING".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.3_f64); -} - -#[tokio::test] -async fn round_negative_float_ceiling_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("CEILING".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.2_f64); -} - -#[tokio::test] -async fn round_positive_float_floor_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("FLOOR".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); -} - -#[tokio::test] -async fn round_negative_float_floor_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("FLOOR".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); -} - -#[tokio::test] -async fn round_positive_float_half_up_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.25).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.3_f64); -} - -#[tokio::test] -async fn round_negative_float_half_up_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.35).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_UP".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.4_f64); -} - -#[tokio::test] -async fn round_positive_float_half_down_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.25).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); -} - -#[tokio::test] -async fn round_negative_float_half_down_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.35).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_DOWN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); -} - -#[tokio::test] -async fn round_positive_float_half_even_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.249).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.25).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.2_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(1.222335).unwrap()), - VariableValue::Integer(Integer::from(5)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1.22234_f64); -} - -#[tokio::test] -async fn round_negative_float_half_even_mode_with_precision() { - let round = Round {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.251).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.3_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.35).unwrap()), - VariableValue::Integer(Integer::from(1)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.4_f64); - - let args = vec![ - VariableValue::Float(Float::from_f64(-1.23455).unwrap()), - VariableValue::Integer(Integer::from(4)), - VariableValue::String("HALF_EVEN".to_string()), - ]; - - let result = round - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1.2346_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Round; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +fn get_func_expr() -> ast::FunctionExpression { + ast::FunctionExpression { + name: Arc::from("round"), + args: vec![], + position_in_query: 10, + } +} + +#[tokio::test] +async fn round_too_few_args() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + + let result = round.call(&context, &get_func_expr(), args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount, + } + )); +} + +#[tokio::test] +async fn round_too_many_args() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(0)), + ]; + + let result = round.call(&context, &get_func_expr(), args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount, + } + )); +} + +#[tokio::test] +async fn round_null() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Null]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, VariableValue::Null); +} + +#[tokio::test] +async fn round_zero_float() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn round_negative_int() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-1_f64).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn round_negative_float_up() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-0.001).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn round_negative_float_down() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-0.7).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1_f64); +} + +#[tokio::test] +async fn round_positive_int() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1_f64).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1_f64); +} + +#[tokio::test] +async fn round_positive_float_up() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1.6).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 2_f64); +} + +#[tokio::test] +async fn round_positive_float_down() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1_f64); +} + +#[tokio::test] +async fn round_positive_float_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(3.1415926).unwrap()), + VariableValue::Integer(Integer::from(3)), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 3.142_f64); +} + +#[tokio::test] +async fn round_negative_float_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.5).unwrap()), + VariableValue::Integer(Integer::from(0)), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.0_f64); +} + +#[tokio::test] +async fn round_negative_float_ties_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.5555).unwrap()), + VariableValue::Integer(Integer::from(3)), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.555_f64); +} + +#[tokio::test] +async fn round_positive_float_up_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.3_f64); +} + +#[tokio::test] +async fn round_negative_float_up_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.35).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.4_f64); +} + +#[tokio::test] +async fn round_positive_float_down_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); +} + +#[tokio::test] +async fn round_negative_float_down_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.2_f64); +} + +#[tokio::test] +async fn round_positive_float_ceiling_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("CEILING".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.3_f64); +} + +#[tokio::test] +async fn round_negative_float_ceiling_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("CEILING".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.2_f64); +} + +#[tokio::test] +async fn round_positive_float_floor_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("FLOOR".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); +} + +#[tokio::test] +async fn round_negative_float_floor_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("FLOOR".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); +} + +#[tokio::test] +async fn round_positive_float_half_up_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.25).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.3_f64); +} + +#[tokio::test] +async fn round_negative_float_half_up_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.35).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_UP".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.4_f64); +} + +#[tokio::test] +async fn round_positive_float_half_down_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.25).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); +} + +#[tokio::test] +async fn round_negative_float_half_down_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.35).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_DOWN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); +} + +#[tokio::test] +async fn round_positive_float_half_even_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.249).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.25).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.2_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(1.222335).unwrap()), + VariableValue::Integer(Integer::from(5)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1.22234_f64); +} + +#[tokio::test] +async fn round_negative_float_half_even_mode_with_precision() { + let round = Round {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.251).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.3_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.35).unwrap()), + VariableValue::Integer(Integer::from(1)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.4_f64); + + let args = vec![ + VariableValue::Float(Float::from_f64(-1.23455).unwrap()), + VariableValue::Integer(Integer::from(4)), + VariableValue::String("HALF_EVEN".to_string()), + ]; + + let result = round + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1.2346_f64); +} diff --git a/core/src/evaluation/functions/numeric/tests/sign_tests.rs b/core/src/evaluation/functions/numeric/tests/sign_tests.rs index da759ad..52ac27e 100644 --- a/core/src/evaluation/functions/numeric/tests/sign_tests.rs +++ b/core/src/evaluation/functions/numeric/tests/sign_tests.rs @@ -1,166 +1,174 @@ -use std::sync::Arc; - -use drasi_query_ast::ast; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::numeric::Sign; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::float::Float; -use crate::evaluation::variable_value::integer::Integer; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -fn get_func_expr() -> ast::FunctionExpression { - ast::FunctionExpression { - name: Arc::from("sign"), - args: vec![], - position_in_query: 10, - } -} - -#[tokio::test] -async fn sign_too_few_args() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - - let result = sign.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn sign_too_many_args() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(Integer::from(0)), - VariableValue::Integer(Integer::from(0)), - ]; - - let result = sign.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn sign_null() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Null]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, VariableValue::Null); -} - -#[tokio::test] -async fn sign_zero_int() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(Integer::from(0))]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 0); -} - -#[tokio::test] -async fn sign_negative_int() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(Integer::from(-1))]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, -1); -} - -#[tokio::test] -async fn sign_positive_int() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(Integer::from(1))]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1); -} - -#[tokio::test] -async fn sign_zero_float() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 0_f64); -} - -#[tokio::test] -async fn sign_negative_float() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(-1.001).unwrap())]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(-1_f64, result); -} - -#[tokio::test] -async fn sign_positive_float() { - let sign = Sign {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; - - let result = sign - .call(&context, &get_func_expr(), args.clone()) - .await - .unwrap(); - assert_eq!(result, 1_f64); -} +use std::sync::Arc; + +use drasi_query_ast::ast; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::numeric::Sign; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::float::Float; +use crate::evaluation::variable_value::integer::Integer; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +fn get_func_expr() -> ast::FunctionExpression { + ast::FunctionExpression { + name: Arc::from("sign"), + args: vec![], + position_in_query: 10, + } +} + +#[tokio::test] +async fn sign_too_few_args() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + + let result = sign.call(&context, &get_func_expr(), args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn sign_too_many_args() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(Integer::from(0)), + VariableValue::Integer(Integer::from(0)), + ]; + + let result = sign.call(&context, &get_func_expr(), args.clone()).await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn sign_null() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Null]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, VariableValue::Null); +} + +#[tokio::test] +async fn sign_zero_int() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(Integer::from(0))]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 0); +} + +#[tokio::test] +async fn sign_negative_int() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(Integer::from(-1))]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, -1); +} + +#[tokio::test] +async fn sign_positive_int() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(Integer::from(1))]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1); +} + +#[tokio::test] +async fn sign_zero_float() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(0_f64).unwrap())]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 0_f64); +} + +#[tokio::test] +async fn sign_negative_float() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(-1.001).unwrap())]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(-1_f64, result); +} + +#[tokio::test] +async fn sign_positive_float() { + let sign = Sign {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Float(Float::from_f64(1.001).unwrap())]; + + let result = sign + .call(&context, &get_func_expr(), args.clone()) + .await + .unwrap(); + assert_eq!(result, 1_f64); +} diff --git a/core/src/evaluation/functions/past/mod.rs b/core/src/evaluation/functions/past/mod.rs index f3feb58..b36e3be 100644 --- a/core/src/evaluation/functions/past/mod.rs +++ b/core/src/evaluation/functions/past/mod.rs @@ -7,7 +7,7 @@ use drasi_query_ast::ast; use futures::StreamExt; use crate::{ - evaluation::{EvaluationError, ExpressionEvaluationContext}, + evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}, interface::ElementArchiveIndex, }; @@ -45,18 +45,24 @@ impl ScalarFunction for GetVersionByTimestamp { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "getVersionByTimestamp".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let metadata = match &args[0] { VariableValue::Element(e) => e.get_metadata(), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let timestamp = match &args[1] { @@ -67,15 +73,34 @@ impl ScalarFunction for GetVersionByTimestamp { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; - let element = self + let element = match self .archive_index .get_element_as_at(&metadata.reference, timestamp) - .await?; + .await + { + Ok(e) => e, + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; match element { Some(e) => Ok(e.to_expression_variable()), @@ -93,17 +118,23 @@ impl ScalarFunction for GetVersionsByTimeRange { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() < 3 || args.len() > 4 { - return Err(EvaluationError::InvalidArgumentCount( - "getVersionsByTimeRange".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let metadata = match &args[0] { VariableValue::Element(e) => e.get_metadata(), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let from = match &args[1] { @@ -114,9 +145,19 @@ impl ScalarFunction for GetVersionsByTimeRange { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; let to = match &args[2] { @@ -127,15 +168,30 @@ impl ScalarFunction for GetVersionsByTimeRange { VariableValue::ZonedDateTime(d) => d.datetime().timestamp_millis() as u64, VariableValue::Integer(n) => match n.as_u64() { Some(u) => u, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } }, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }) + } }; let retrieve_initial_value = match args.get(3) { Some(VariableValue::Bool(b)) => *b, None => false, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(3), + }) + } }; let range = match retrieve_initial_value { @@ -149,10 +205,19 @@ impl ScalarFunction for GetVersionsByTimeRange { }, }; - let mut stream = self + let mut stream = match self .archive_index .get_element_versions(&metadata.reference, range) - .await?; + .await + { + Ok(s) => s, + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } + }; let mut result = Vec::new(); while let Some(item) = stream.next().await { @@ -172,7 +237,12 @@ impl ScalarFunction for GetVersionsByTimeRange { result.push(e.to_expression_variable()) } } - Err(e) => return Err(EvaluationError::IndexError(e)), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::IndexError(e), + }) + } } } diff --git a/core/src/evaluation/functions/temporal_duration/temporal_duration.rs b/core/src/evaluation/functions/temporal_duration/temporal_duration.rs index f4ad7a4..5611edf 100644 --- a/core/src/evaluation/functions/temporal_duration/temporal_duration.rs +++ b/core/src/evaluation/functions/temporal_duration/temporal_duration.rs @@ -2,11 +2,12 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::temporal_constants; use crate::evaluation::variable_value::duration::Duration; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; -use chrono::{Datelike, Duration as ChronoDuration, LocalResult, NaiveDate, NaiveTime}; +use chrono::{Datelike, Duration as ChronoDuration, LocalResult, NaiveDate}; use iso8601_duration::Duration as IsoDuration; use log::error; use regex::Regex; @@ -20,20 +21,21 @@ impl ScalarFunction for DurationFunc { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "duration".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => { let duration_str = s.as_str(); let duration = match parse_duration_input(duration_str).await { Ok(duration) => duration, - Err(_) => return Err(EvaluationError::ParseError), + Err(e) => return Err(e), }; Ok(VariableValue::Duration(duration)) } @@ -58,47 +60,130 @@ impl ScalarFunction for DurationFunc { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in duration object"); - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } let year = match o.get("years") { - Some(f) => f.as_f64().unwrap(), + Some(f) => match f.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }) + } + }, _ => 0.0, }; let week = match o.get("weeks") { - Some(f) => f.as_f64().unwrap(), + Some(f) => match f.as_f64() { + Some(f) => f, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => 0.0, }; let month = match o.get("months") { - Some(f) => f.as_f64().unwrap() + year.fract() * 12.0, + Some(f) => match f.as_f64() { + Some(f) => f + year.fract() * 12.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => year.fract() * 12.0, }; let day = match o.get("days") { - Some(f) => f.as_f64().unwrap() + month.fract() * 30.0, + Some(f) => match f.as_f64() { + Some(f) => f + month.fract() * 30.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => month.fract() * 30.0, }; let hour = match o.get("hours") { - Some(f) => f.as_f64().unwrap() + day.fract() * 24.0, + Some(f) => match f.as_f64() { + Some(f) => f + day.fract() * 24.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => day.fract() * 24.0, }; let minute = match o.get("minutes") { - Some(f) => f.as_f64().unwrap() + hour.fract() * 60.0, + Some(f) => match f.as_f64() { + Some(f) => f + hour.fract() * 60.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => hour.fract() * 60.0, }; let second = match o.get("seconds") { - Some(f) => f.as_f64().unwrap() + minute.fract() * 60.0, + Some(f) => match f.as_f64() { + Some(f) => f + minute.fract() * 60.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => minute.fract() * 60.0, }; let millisecond = match o.get("milliseconds") { - Some(f) => f.as_f64().unwrap() + second.fract() * 1000.0, + Some(f) => match f.as_f64() { + Some(f) => f + second.fract() * 1000.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => second.fract() * 1000.0, }; let microsecond = match o.get("microseconds") { - Some(f) => f.as_f64().unwrap() + millisecond.fract() * 1000.0, + Some(f) => match f.as_f64() { + Some(f) => f + millisecond.fract() * 1000.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => millisecond.fract() * 1000.0, }; let nanosecond = match o.get("nanoseconds") { - Some(f) => f.as_f64().unwrap() + microsecond.fract() * 1000.0, + Some(f) => match f.as_f64() { + Some(f) => f + microsecond.fract() * 1000.0, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }, _ => microsecond.fract() * 1000.0, }; @@ -114,7 +199,10 @@ impl ScalarFunction for DurationFunc { Duration::new(chrono_duration, year.floor() as i64, month.floor() as i64); Ok(VariableValue::Duration(duration)) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -127,11 +215,14 @@ impl ScalarFunction for Between { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("between".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1]) { (VariableValue::Date(start), VariableValue::Date(end)) => { @@ -143,7 +234,7 @@ impl ScalarFunction for Between { ))) } (VariableValue::Date(_start), VariableValue::LocalTime(end)) => { - let start_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let start_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; let duration = end.signed_duration_since(start_time); Ok(VariableValue::Duration(Duration::new( ChronoDuration::from(duration), @@ -152,7 +243,10 @@ impl ScalarFunction for Between { ))) } (VariableValue::Date(start), VariableValue::LocalDateTime(end)) => { - let start_datetime = start.and_hms_opt(0, 0, 0).unwrap(); + let start_datetime = match start.and_hms_opt(0, 0, 0) { + Some(start_time) => start_time, + None => unreachable!(), + }; let duration = end.signed_duration_since(start_datetime); Ok(VariableValue::Duration(Duration::new( ChronoDuration::from(duration), @@ -161,7 +255,7 @@ impl ScalarFunction for Between { ))) } (VariableValue::Date(_start), VariableValue::ZonedTime(end)) => { - let start_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let start_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; let end_time = end.time(); let duration = end_time.signed_duration_since(start_time); Ok(VariableValue::Duration(Duration::new( @@ -173,11 +267,19 @@ impl ScalarFunction for Between { (VariableValue::Date(start), VariableValue::ZonedDateTime(end)) => { let start_time = match start.and_hms_opt(0, 0, 0) { Some(start_time) => start_time, - None => return Err(EvaluationError::InvalidType), + None => unreachable!(), }; let start_datetime = match start_time.and_local_timezone(*end.datetime().offset()) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let end_datetime = end.datetime().fixed_offset(); let duration = end_datetime.signed_duration_since(start_datetime); @@ -196,7 +298,7 @@ impl ScalarFunction for Between { ))) } (VariableValue::LocalTime(start), VariableValue::Date(_end)) => { - let end_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let end_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; let duration = end_time.signed_duration_since(*start); Ok(VariableValue::Duration(Duration::new( ChronoDuration::from(duration), @@ -231,7 +333,15 @@ impl ScalarFunction for Between { .and_local_timezone(*end_datetime.offset()) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(start_datetime); Ok(VariableValue::Duration(Duration::new( @@ -241,20 +351,33 @@ impl ScalarFunction for Between { ))) } (VariableValue::ZonedTime(start), VariableValue::ZonedTime(end)) => { - let dummy_date = NaiveDate::from_ymd_opt(2020, 1, 1).unwrap(); + let dummy_date = match NaiveDate::from_ymd_opt(2020, 1, 1) { + Some(date) => date, + None => unreachable!(), + }; let start_datetime = match dummy_date .and_time(*start.time()) .and_local_timezone(*start.offset()) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let end_datetime = match dummy_date .and_time(*end.time()) .and_local_timezone(*end.offset()) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; let start_time = start_datetime.fixed_offset(); @@ -267,7 +390,7 @@ impl ScalarFunction for Between { ))) } (VariableValue::ZonedTime(start), VariableValue::Date(_end)) => { - let end_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let end_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; let start_time = start.time(); let duration = end_time.signed_duration_since(*start_time); @@ -295,7 +418,15 @@ impl ScalarFunction for Between { .and_local_timezone(*start_offset) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let start_datetime = match end .date() @@ -303,7 +434,15 @@ impl ScalarFunction for Between { .and_local_timezone(*start_offset) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(start_datetime); @@ -321,7 +460,15 @@ impl ScalarFunction for Between { .and_local_timezone(*start.offset()) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let fixed_start_datetime = start_datetime.fixed_offset(); @@ -342,7 +489,10 @@ impl ScalarFunction for Between { ))) } (VariableValue::LocalDateTime(start), VariableValue::Date(end)) => { - let end_datetime = end.and_hms_opt(0, 0, 0).unwrap(); + let end_datetime = match end.and_hms_opt(0, 0, 0) { + Some(end_time) => end_time, + None => unreachable!(), + }; let duration = end_datetime.signed_duration_since(*start); Ok(VariableValue::Duration(Duration::new( ChronoDuration::from(duration), @@ -368,11 +518,27 @@ impl ScalarFunction for Between { .and_local_timezone(*offset) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let start_datetime = match start.and_local_timezone(*offset) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(start_datetime); @@ -386,7 +552,15 @@ impl ScalarFunction for Between { let start_offset = end.datetime().offset(); let start_datetime = match start.and_local_timezone(*start_offset) { LocalResult::Single(start_datetime) => start_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end.datetime().signed_duration_since(start_datetime); @@ -412,7 +586,15 @@ impl ScalarFunction for Between { .and_local_timezone(*start_datetime.offset()) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(start_datetime); Ok(VariableValue::Duration(Duration::new( @@ -429,7 +611,15 @@ impl ScalarFunction for Between { .and_local_timezone(*start_datetime.offset()) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(start_datetime); Ok(VariableValue::Duration(Duration::new( @@ -446,7 +636,15 @@ impl ScalarFunction for Between { .and_local_timezone(*end.offset()) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let fixed_end_datetime = end_datetime.fixed_offset(); @@ -462,7 +660,15 @@ impl ScalarFunction for Between { let start_offset = start.datetime().offset(); let end_datetime = match end.and_local_timezone(*start_offset) { LocalResult::Single(end_datetime) => end_datetime, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = end_datetime.signed_duration_since(*start.datetime()); @@ -473,7 +679,10 @@ impl ScalarFunction for Between { ))) } (VariableValue::Null, _) | (_, VariableValue::Null) => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -486,24 +695,21 @@ impl ScalarFunction for InMonths { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "in_months".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1]) { (VariableValue::Date(start), VariableValue::Date(end)) => { let end_date = end; let start_date = start; - let (years_diff, months_diff) = - match calculate_year_month(start_date, end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(start_date, end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -520,11 +726,7 @@ impl ScalarFunction for InMonths { let end_date = end.date(); let start_date = start; - let (years_diff, months_diff) = - match calculate_year_month(start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -535,11 +737,7 @@ impl ScalarFunction for InMonths { let end_date = end.datetime().date_naive(); let start_date = start; - let (years_diff, months_diff) = - match calculate_year_month(start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -564,11 +762,7 @@ impl ScalarFunction for InMonths { let end_date = end.date(); let start_date = start.date(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -583,11 +777,7 @@ impl ScalarFunction for InMonths { let end_date = end; let start_date = start.date(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -598,11 +788,7 @@ impl ScalarFunction for InMonths { let end_date = end.datetime().date_naive(); let start_date = start.date(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -613,11 +799,7 @@ impl ScalarFunction for InMonths { let end_date = end.datetime().fixed_offset().date_naive(); let start_date = start.datetime().fixed_offset().date_naive(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -628,11 +810,7 @@ impl ScalarFunction for InMonths { let end_date = end.date(); let start_date = start.datetime().fixed_offset().date_naive(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, &end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, &end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -643,11 +821,7 @@ impl ScalarFunction for InMonths { let end_date = end; let start_date = start.datetime().fixed_offset().date_naive(); - let (years_diff, months_diff) = - match calculate_year_month(&start_date, end_date).await { - Ok((years_diff, months_diff)) => (years_diff, months_diff), - Err(_) => return Err(EvaluationError::InvalidType), - }; + let (years_diff, months_diff) = calculate_year_month(&start_date, end_date); Ok(VariableValue::Duration(Duration::new( ChronoDuration::days(0), years_diff, @@ -658,7 +832,10 @@ impl ScalarFunction for InMonths { | (VariableValue::ZonedDateTime(_), VariableValue::ZonedTime(_)) => Ok( VariableValue::Duration(Duration::new(ChronoDuration::days(0), 0, 0)), ), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -673,21 +850,40 @@ impl ScalarFunction for InDays { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("in_days".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let between = Between {}; let result = between.call(_context, expression, args.clone()).await; match result { Ok(return_value) => { - let days = return_value.as_duration().unwrap().duration().num_days(); + let days = match return_value.as_duration() { + Some(duration) => duration.duration().num_days(), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let duration = VariableValue::Duration(Duration::new(ChronoDuration::days(days), 0, 0)); Ok(duration) } - Err(_error) => Err(EvaluationError::InvalidType), + Err(_error) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "Duration".to_string(), + }, + }), } } } @@ -702,25 +898,40 @@ impl ScalarFunction for InSeconds { _context: &ExpressionEvaluationContext, expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount( - "in_seconds".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } let between = Between {}; let result = between.call(_context, expression, args.clone()).await; match result { Ok(return_value) => { - let seconds = match return_value - .as_duration() - .unwrap() - .duration() - .num_nanoseconds() - { - Some(seconds) => seconds, - None => return Err(EvaluationError::InvalidType), + let seconds = match return_value.as_duration() { + Some(duration) => match duration.duration().num_nanoseconds() { + Some(seconds) => seconds, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }) + } + }, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }) + } }; let duration = VariableValue::Duration(Duration::new( ChronoDuration::nanoseconds(seconds), @@ -729,15 +940,17 @@ impl ScalarFunction for InSeconds { )); Ok(duration) } - Err(_error) => Err(EvaluationError::InvalidType), + Err(_error) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "Duration".to_string(), + }, + }), } } } -async fn calculate_year_month( - start: &NaiveDate, - end: &NaiveDate, -) -> Result<(i64, i64), EvaluationError> { +fn calculate_year_month(start: &NaiveDate, end: &NaiveDate) -> (i64, i64) { let months_diff = end.month() as i64 - start.month() as i64; let years_diff = (end.year() - start.year()) as i64; @@ -748,16 +961,23 @@ async fn calculate_year_month( result_month = (years_diff - 1) * 12 + (months_diff + 12); } - Ok((result_month / 12, result_month % 12)) + (result_month / 12, result_month % 12) } -async fn parse_duration_input(duration_str: &str) -> Result { +async fn parse_duration_input(duration_str: &str) -> Result { let mut duration_result = ChronoDuration::days(0); //Durtion string must start with 'P' let duration = match duration_str.strip_prefix('P') { Some(duration) => duration, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR.to_string(), + }, + }) + } }; let date_duration = duration.split('T').next(); @@ -772,7 +992,17 @@ async fn parse_duration_input(duration_str: &str) -> Result { let pattern = r"(\d{1,19}Y)?(\d{1,19}M)?(\d{1,19}W)?(\d{1,19}(?:\.\d{1,9})?D)?"; - let re = Regex::new(pattern).unwrap(); + let re = match Regex::new(pattern) { + Ok(re) => re, + Err(_) => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "a valid regex".to_string(), + }, + }) + } + }; if let Some(cap) = re.captures(date_duration) { for part in cap.iter().skip(1) { @@ -783,7 +1013,13 @@ async fn parse_duration_input(duration_str: &str) -> Result() { duration_years = years; } else { - return Err(EvaluationError::ParseError); + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }); } } if matched_value.contains('M') { @@ -791,7 +1027,13 @@ async fn parse_duration_input(duration_str: &str) -> Result() { duration_months = months; } else { - return Err(EvaluationError::ParseError); + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }); } } if matched_value.contains('W') { @@ -799,7 +1041,13 @@ async fn parse_duration_input(duration_str: &str) -> Result() { duration_result += ChronoDuration::weeks(weeks); } else { - return Err(EvaluationError::ParseError); + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }); } } if matched_value.contains('D') { @@ -810,12 +1058,25 @@ async fn parse_duration_input(duration_str: &str) -> Result() { duration_result += ChronoDuration::days(days); } else { - return Err(EvaluationError::ParseError); + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }); } } } @@ -831,22 +1092,52 @@ async fn parse_duration_input(duration_str: &str) -> Result() { Ok(iso_duration) => iso_duration, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR.to_string(), + }, + }) + } }; let seconds = match iso_duration.num_seconds() { Some(seconds) => seconds, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR.to_string(), + }, + }) + } }; if time_duration_string.contains('.') { let mut fract_string = match time_duration_string.split('.').last() { Some(fract_string) => fract_string, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }) + } }; fract_string = &fract_string[..fract_string.len() - 1]; let nanoseconds = match fract_string.parse::() { Ok(nanoseconds) => nanoseconds, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionError { + function_name: "Duration".to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DURATION_FORMAT_ERROR + .to_string(), + }, + }) + } }; duration_result += ChronoDuration::nanoseconds(nanoseconds * 100_000_000_i64); } diff --git a/core/src/evaluation/functions/temporal_instant/temporal_instant.rs b/core/src/evaluation/functions/temporal_instant/temporal_instant.rs index f1281c7..bba57e8 100644 --- a/core/src/evaluation/functions/temporal_instant/temporal_instant.rs +++ b/core/src/evaluation/functions/temporal_instant/temporal_instant.rs @@ -6,7 +6,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; use chrono::{ prelude::*, Datelike, Days, Duration, FixedOffset, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, Weekday, @@ -26,11 +26,14 @@ impl ScalarFunction for Date { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() > 1 { - return Err(EvaluationError::InvalidArgumentCount("date".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.len() == 0 { // current date @@ -41,11 +44,14 @@ impl ScalarFunction for Date { match &args[0] { VariableValue::String(s) => { let date_str = s.as_str(); - let date = parse_date_string(date_str).await; - match date { + let date = match parse_date_string(date_str).await { Ok(result_date) => Ok(VariableValue::Date(result_date)), - _ => Err(EvaluationError::InvalidType), - } + Err(e) => Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }), + }; + date } VariableValue::Object(o) => { let valid_keys: HashSet = [ @@ -67,31 +73,25 @@ impl ScalarFunction for Date { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in the date object"); - return Err(EvaluationError::InvalidType); - } - if o.get("timezone").is_some() { - let tz = match o.get("timezone") { - Some(tz) => { - let tz_str = tz.as_str().unwrap(); - let tz: Tz = match tz_str.parse() { - Ok(tz) => tz, - Err(_) => return Err(EvaluationError::InvalidType), - }; - tz - } - None => return Err(EvaluationError::InvalidType), - }; - let local: chrono::DateTime = Local::now().with_timezone(&tz); - return Ok(VariableValue::Date(local.date_naive())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } let result = create_date_from_componet(o.clone()).await; match result { - Some(date) => Ok(date), - None => Err(EvaluationError::InvalidType), + Ok(date) => Ok(date), + Err(e) => Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }), } } VariableValue::Date(d) => Ok(VariableValue::Date(*d)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -104,13 +104,14 @@ impl ScalarFunction for LocalTime { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() > 1 { - return Err(EvaluationError::InvalidArgumentCount( - "localtime".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.len() == 0 { let local = Local::now().time(); @@ -122,7 +123,13 @@ impl ScalarFunction for LocalTime { let time = parse_local_time_input(time_str).await; match time { Ok(result_time) => Ok(VariableValue::LocalTime(result_time)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR + .to_string(), + }, + }), } } VariableValue::Object(o) => { @@ -142,16 +149,25 @@ impl ScalarFunction for LocalTime { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in the localtime object"); - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } let result = create_time_from_componet(o.clone()).await; match result { - Some(time) => Ok(time), - None => Err(EvaluationError::InvalidType), + Ok(time) => Ok(time), + Err(e) => Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }), } } VariableValue::LocalTime(t) => Ok(VariableValue::LocalTime(*t)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -164,13 +180,14 @@ impl ScalarFunction for LocalDateTime { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() > 1 { - return Err(EvaluationError::InvalidArgumentCount( - "localdatetime".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.len() == 0 { // current datetime @@ -184,7 +201,13 @@ impl ScalarFunction for LocalDateTime { let datetime = parse_local_date_time_input(datetime_str).await; match datetime { Ok(result_datetime) => Ok(VariableValue::LocalDateTime(result_datetime)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), } } VariableValue::Object(o) => { @@ -213,20 +236,49 @@ impl ScalarFunction for LocalDateTime { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in the localdatetime object"); - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } if o.get("timezone").is_some() { // retrieve the naivedatetime from the timezone let tz = match o.get("timezone") { Some(tz) => { - let tz_str = tz.as_str().unwrap(); + let tz_str = match tz.as_str() { + Some(tz_str) => tz_str, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + }; let tz: Tz = match tz_str.parse() { Ok(tz) => tz, - Err(_) => return Err(EvaluationError::InvalidType), + Err(_) => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), }; tz } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let local: chrono::DateTime = Local::now().with_timezone(&tz); @@ -234,21 +286,51 @@ impl ScalarFunction for LocalDateTime { } let date_result = match create_date_from_componet(o.clone()).await { - Some(date) => date, - None => return Err(EvaluationError::InvalidType), + Ok(date) => date, + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let time_result = match create_time_from_componet(o.clone()).await { + Ok(time) => time, + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } + }; + let local_time = match time_result.as_local_time() { Some(time) => time, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; + let datetime = match date_result.as_date() { + Some(date) => date.and_time(local_time), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; - let datetime = date_result - .as_date() - .unwrap() - .and_time(time_result.as_local_time().unwrap()); Ok(VariableValue::LocalDateTime(datetime)) } VariableValue::LocalDateTime(dt) => Ok(VariableValue::LocalDateTime(*dt)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -261,15 +343,18 @@ impl ScalarFunction for Time { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() > 1 { - return Err(EvaluationError::InvalidArgumentCount("time".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.len() == 0 { let local = Local::now().time(); - let timezone = FixedOffset::east_opt(0).unwrap(); + let timezone = *temporal_constants::UTC_FIXED_OFFSET; return Ok(VariableValue::ZonedTime(ZonedTime::new(local, timezone))); } match &args[0] { @@ -278,7 +363,13 @@ impl ScalarFunction for Time { let time = parse_zoned_time_input(time_str).await; match time { Ok(result_time) => Ok(VariableValue::ZonedTime(result_time)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }), } } VariableValue::Object(o) => { @@ -299,7 +390,10 @@ impl ScalarFunction for Time { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in the time object"); - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } let timezone = match o.get("timezone") { @@ -310,7 +404,16 @@ impl ScalarFunction for Time { }; match handle_iana_timezone(tz_str).await { Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } } } None => Tz::UTC, @@ -328,16 +431,34 @@ impl ScalarFunction for Time { let result = create_time_from_componet(o.clone()).await; match result { - Some(time) => { + Ok(time) => { let dummy_date = *temporal_constants::EPOCH_NAIVE_DATE; let local_time = match time.as_local_time() { Some(time) => time, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let datetime = dummy_date.and_time(local_time); let zoned_datetime = match timezone.from_local_datetime(&datetime) { LocalResult::Single(zoned_datetime) => zoned_datetime.fixed_offset(), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let zoned_time = zoned_datetime.time(); let offset = zoned_datetime.offset(); @@ -347,10 +468,18 @@ impl ScalarFunction for Time { _ => "UTC", }; if timezone_str.contains('+') || timezone_str.contains('-') { - let offset = match FixedOffset::from_str(timezone_str) { - Ok(offset) => offset, - Err(_) => return Err(EvaluationError::ParseError), - }; + let offset = + match FixedOffset::from_str(timezone_str) { + Ok(offset) => offset, + Err(_) => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_TIME_FORMAT_ERROR + .to_string(), + }, + }), + }; return Ok(VariableValue::ZonedTime(ZonedTime::new( zoned_time, offset, ))); @@ -359,11 +488,17 @@ impl ScalarFunction for Time { zoned_time, *offset, ))); } - None => Err(EvaluationError::InvalidType), + Err(e) => Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }), } } VariableValue::ZonedTime(t) => Ok(VariableValue::ZonedTime(*t)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -376,18 +511,19 @@ impl ScalarFunction for DateTime { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() > 1 { - return Err(EvaluationError::InvalidArgumentCount( - "datetime".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } if args.len() == 0 { let local = Local::now(); - let datetime = local.with_timezone(&FixedOffset::east_opt(0).unwrap()); + let datetime = local.with_timezone(&*temporal_constants::UTC_FIXED_OFFSET); return Ok(VariableValue::ZonedDateTime(ZonedDateTime::new( datetime, None, ))); @@ -398,7 +534,13 @@ impl ScalarFunction for DateTime { let time = parse_zoned_date_time_input(time_str).await; match time { Ok(result_time) => Ok(VariableValue::ZonedDateTime(result_time)), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), } } VariableValue::Object(o) => { @@ -429,48 +571,112 @@ impl ScalarFunction for DateTime { o.keys().filter(|&key| !valid_keys.contains(key)).collect(); if !invalid_keys.is_empty() { error!("Invalid keys in the datetime object"); - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }); } if o.contains_key("epochSeconds") || o.contains_key("epochMillis") { let result = match create_date_time_from_epoch(o.clone()).await { Some(datetime) => datetime, - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; return Ok(result); } - let timezone = match o.get("timezone") { - Some(tz) => { - let tz_str = tz.as_str().unwrap(); - match handle_iana_timezone(tz_str).await { - Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + let timezone = + match o.get("timezone") { + Some(tz) => { + let tz_str = match tz.as_str() { + Some(tz_str) => tz_str, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + }; + match handle_iana_timezone(tz_str).await { + Some(tz) => tz, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + } } - } - None => Tz::UTC, - }; + None => Tz::UTC, + }; if o.get("timezone").is_some() && o.len() == 1 { let local: chrono::DateTime = Local::now().with_timezone(&timezone); let zoned_time = match timezone.from_local_datetime(&local.naive_local()) { LocalResult::Single(zoned_time) => zoned_time.fixed_offset(), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; return Ok(VariableValue::ZonedDateTime(ZonedDateTime::new( zoned_time, None, ))); } let naive_date = match create_date_from_componet(o.clone()).await { - Some(date) => date.as_date().unwrap(), - None => return Err(EvaluationError::InvalidType), + Ok(date) => match date.as_date() { + Some(date) => date, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }, + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let result = create_time_from_componet(o.clone()).await; match result { - Some(time) => { - let local_time = match time.as_local_time() { - Some(time) => time, - None => return Err(EvaluationError::InvalidType), - }; + Ok(time) => { + let local_time = + match time.as_local_time() { + Some(time) => time, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + }; let datetime = naive_date.and_time(local_time); let timezone_str = match o.get("timezone") { Some(VariableValue::String(s)) => s.as_str(), @@ -479,11 +685,25 @@ impl ScalarFunction for DateTime { if timezone_str.contains('+') || timezone_str.contains('-') { let offset = match FixedOffset::from_str(timezone_str) { Ok(offset) => offset, - Err(_) => return Err(EvaluationError::InvalidType), + Err(_) => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), }; let datetime_offset = match datetime.and_local_timezone(offset) { LocalResult::Single(offset) => offset.fixed_offset(), - _ => return Err(EvaluationError::InvalidType), + _ => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), }; return Ok(VariableValue::ZonedDateTime(ZonedDateTime::new( datetime_offset, @@ -492,73 +712,141 @@ impl ScalarFunction for DateTime { } let zoned_datetime = match timezone.from_local_datetime(&datetime) { LocalResult::Single(zoned_datetime) => zoned_datetime.fixed_offset(), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; return Ok(VariableValue::ZonedDateTime(ZonedDateTime::new( zoned_datetime, Some(timezone_str.to_string()), ))); } - None => Err(EvaluationError::InvalidType), + Err(e) => Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }), } } VariableValue::ZonedDateTime(t) => Ok(VariableValue::ZonedDateTime(t.clone())), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } -async fn create_date_from_componet(o: BTreeMap) -> Option { +async fn create_date_from_componet( + o: BTreeMap, +) -> Result { let year = match o.get("year") { - Some(year) => year.as_i64().unwrap(), + Some(year) => match year.as_i64() { + Some(year) => year, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let week = match o.get("week") { - Some(week) => week.as_i64().unwrap(), + Some(week) => match week.as_i64() { + Some(week) => week, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; if week > 0 { let day_of_week = match o.get("dayOfWeek") { - Some(day_of_week) => day_of_week.as_i64().unwrap(), + Some(day_of_week) => match day_of_week.as_i64() { + Some(day_of_week) => day_of_week, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; - let begin_date = - NaiveDate::from_isoywd_opt(year as i32, week as u32, Weekday::Mon).unwrap(); + let begin_date = match NaiveDate::from_isoywd_opt(year as i32, week as u32, Weekday::Mon) { + Some(begin_date) => begin_date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; let date = begin_date + Duration::days(day_of_week - 1); - return Some(VariableValue::Date(date)); + return Ok(VariableValue::Date(date)); } let ordinal_day = match o.get("ordinalDay") { - Some(ordinal_day) => ordinal_day.as_i64().unwrap(), + Some(ordinal_day) => match ordinal_day.as_i64() { + Some(ordinal_day) => ordinal_day, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; if ordinal_day > 0 { - let date = NaiveDate::from_yo_opt(year as i32, ordinal_day as u32).unwrap(); - return Some(VariableValue::Date(date)); + let date = match NaiveDate::from_yo_opt(year as i32, ordinal_day as u32) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; + return Ok(VariableValue::Date(date)); } let quarter = match o.get("quarter") { - Some(quarter) => quarter.as_i64().unwrap(), + Some(quarter) => match quarter.as_i64() { + Some(quarter) => quarter, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; if quarter > 0 { let day_of_quarter = match o.get("dayOfQuarter") { - Some(day_of_quarter) => day_of_quarter.as_i64().unwrap(), + Some(day_of_quarter) => match day_of_quarter.as_i64() { + Some(day_of_quarter) => day_of_quarter, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let month = (quarter - 1) * 3 + 1; - let begin_date = NaiveDate::from_ymd_opt(year as i32, month as u32, 1).unwrap(); + let begin_date = match NaiveDate::from_ymd_opt(year as i32, month as u32, 1) { + Some(begin_date) => begin_date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; let date = begin_date + Duration::days(day_of_quarter - 1); - return Some(VariableValue::Date(date)); + return Ok(VariableValue::Date(date)); } let month = match o.get("month") { - Some(month) => month.as_i64().unwrap(), + Some(month) => match month.as_i64() { + Some(month) => month, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let day = match o.get("day") { - Some(day) => day.as_i64().unwrap(), + Some(day) => match day.as_i64() { + Some(day) => day, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; - let date = NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32).unwrap(); - Some(VariableValue::Date(date)) + let date = match NaiveDate::from_ymd_opt(year as i32, month as u32, day as u32) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; + Ok(VariableValue::Date(date)) } async fn create_date_time_from_epoch(o: BTreeMap) -> Option { @@ -571,12 +859,15 @@ async fn create_date_time_from_epoch(o: BTreeMap) -> Opti Some(epoch_seconds) => epoch_seconds.as_i64().unwrap_or(0), None => 0, }; - let datetime = - NaiveDateTime::from_timestamp_opt(epoch_seconds, nanoseconds as u32).unwrap(); - let zoned_datetime = match datetime.and_local_timezone(FixedOffset::east_opt(0).unwrap()) { - LocalResult::Single(zoned_datetime) => zoned_datetime, - _ => return None, + let datetime = match NaiveDateTime::from_timestamp_opt(epoch_seconds, nanoseconds as u32) { + Some(datetime) => datetime, + None => return None, }; + let zoned_datetime = + match datetime.and_local_timezone(*temporal_constants::UTC_FIXED_OFFSET) { + LocalResult::Single(zoned_datetime) => zoned_datetime, + _ => return None, + }; return Some(VariableValue::ZonedDateTime(ZonedDateTime::new( zoned_datetime, None, @@ -591,10 +882,11 @@ async fn create_date_time_from_epoch(o: BTreeMap) -> Opti None => return None, }; let datetime = datetime_epoch + Duration::nanoseconds(nanoseconds); - let zoned_datetime = match datetime.and_local_timezone(FixedOffset::east_opt(0).unwrap()) { - LocalResult::Single(zoned_datetime) => zoned_datetime, - _ => return None, - }; + let zoned_datetime = + match datetime.and_local_timezone(*temporal_constants::UTC_FIXED_OFFSET) { + LocalResult::Single(zoned_datetime) => zoned_datetime, + _ => return None, + }; return Some(VariableValue::ZonedDateTime(ZonedDateTime::new( zoned_datetime, None, @@ -604,38 +896,65 @@ async fn create_date_time_from_epoch(o: BTreeMap) -> Opti None } -async fn create_time_from_componet(o: BTreeMap) -> Option { +async fn create_time_from_componet( + o: BTreeMap, +) -> Result { let hour = match o.get("hour") { - Some(hour) => hour.as_i64().unwrap_or(0), + Some(hour) => match hour.as_i64() { + Some(hour) => hour, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let minute = match o.get("minute") { - Some(minute) => minute.as_i64().unwrap_or(0), + Some(minute) => match minute.as_i64() { + Some(minute) => minute, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let second = match o.get("second") { - Some(second) => second.as_i64().unwrap_or(0), + Some(second) => match second.as_i64() { + Some(second) => second, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let millisecond = match o.get("millisecond") { - Some(millisecond) => millisecond.as_i64().unwrap_or(0), + Some(millisecond) => match millisecond.as_i64() { + Some(millisecond) => millisecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let microsecond = match o.get("microsecond") { - Some(microsecond) => microsecond.as_i64().unwrap_or(0), + Some(microsecond) => match microsecond.as_i64() { + Some(microsecond) => microsecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let nanosecond = match o.get("nanosecond") { - Some(nanosecond) => nanosecond.as_i64().unwrap_or(0), + Some(nanosecond) => match nanosecond.as_i64() { + Some(nanosecond) => nanosecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; - let mut time = NaiveTime::from_hms_opt(hour as u32, minute as u32, second as u32).unwrap(); + let mut time = match NaiveTime::from_hms_opt(hour as u32, minute as u32, second as u32) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; time = time + Duration::nanoseconds(nanosecond) + Duration::microseconds(microsecond) + Duration::milliseconds(millisecond); - Some(VariableValue::LocalTime(time)) + Ok(VariableValue::LocalTime(time)) } #[derive(Debug, PartialEq)] @@ -646,19 +965,25 @@ impl ScalarFunction for Truncate { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() < 2 || args.len() > 3 { - return Err(EvaluationError::InvalidArgumentCount( - "truncate".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1], &args.get(2)) { (VariableValue::String(s), VariableValue::Date(d), None) => { let truncated_date = match truncate_date(s.to_string(), *d).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; Ok(VariableValue::Date(truncated_date)) } @@ -666,14 +991,24 @@ impl ScalarFunction for Truncate { let truncated_date = match truncate_date_with_map(s.to_string(), *d, m.clone()).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; Ok(VariableValue::Date(truncated_date)) } (VariableValue::String(s), VariableValue::LocalTime(d), None) => { let truncated_time = match truncate_local_time(s.to_string(), *d).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; Ok(VariableValue::LocalTime(truncated_time)) } @@ -685,21 +1020,34 @@ impl ScalarFunction for Truncate { let truncated_time = match truncate_local_time_with_map(s.to_string(), *d, m.clone()).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; Ok(VariableValue::LocalTime(truncated_time)) } (VariableValue::String(s), VariableValue::LocalDateTime(dt), None) => { let truncated_date = match truncate_date(s.to_string(), dt.date()).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; - println!("truncated_date: {:?}", truncated_date); let truncated_time = match truncate_local_time(s.to_string(), dt.time()).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; - println!("truncated_time: {:?}", truncated_time); let truncated_date_time = NaiveDateTime::new(truncated_date, truncated_time); Ok(VariableValue::LocalDateTime(truncated_date_time)) } @@ -711,12 +1059,22 @@ impl ScalarFunction for Truncate { let truncated_date = match truncate_date_with_map(s.to_string(), dt.date(), m.clone()).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let truncated_time = match truncate_local_time_with_map(s.to_string(), dt.time(), m.clone()).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let truncated_date_time = NaiveDateTime::new(truncated_date, truncated_time); Ok(VariableValue::LocalDateTime(truncated_date_time)) @@ -726,7 +1084,12 @@ impl ScalarFunction for Truncate { let offset = *t.offset(); let truncated_time = match truncate_local_time(s.to_string(), naive_time).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let zoned_time_result = ZonedTime::new(truncated_time, offset); Ok(VariableValue::ZonedTime(zoned_time_result)) @@ -746,7 +1109,12 @@ impl ScalarFunction for Truncate { .await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let zoned_time_result = ZonedTime::new(truncated_time, offset); Ok(VariableValue::ZonedTime(zoned_time_result)) @@ -759,18 +1127,37 @@ impl ScalarFunction for Truncate { let offset = datetime.offset(); let truncated_time = match truncate_local_time(s.to_string(), naive_time).await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let truncate_date = match truncate_date(s.to_string(), naive_date).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; let truncated_naive_date_time = NaiveDateTime::new(truncate_date, truncated_time); - let truncated_date_time = - match truncated_naive_date_time.and_local_timezone(*offset) { - LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::InvalidType), - }; + let truncated_date_time = match truncated_naive_date_time + .and_local_timezone(*offset) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let zoned_date_time = ZonedDateTime::new(truncated_date_time, timezone); Ok(VariableValue::ZonedDateTime(zoned_date_time)) } @@ -792,49 +1179,118 @@ impl ScalarFunction for Truncate { .await { Ok(time) => time, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; - println!("truncated_time: {:?}", truncated_time); let truncate_date = match truncate_date_with_map(s.to_string(), naive_date, m.clone()).await { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(e) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: e, + }) + } }; - println!("truncate_date: {:?}", truncate_date); let truncated_naive_date_time = NaiveDateTime::new(truncate_date, truncated_time); if m.get("timezone").is_some() { let timezone = match m.get("timezone") { Some(tz) => { - let tz_str = tz.as_str().unwrap(); + let tz_str = match tz.as_str() { + Some(tz_str) => tz_str, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + }; match handle_iana_timezone(tz_str).await { Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), } } - None => return Err(EvaluationError::InvalidType), + None => unreachable!(), }; let datetime_tz = match timezone.from_local_datetime(&truncated_naive_date_time) { LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } }; let datetime_fixed_offset = datetime_tz.fixed_offset(); - let timezone_string = m.get("timezone").unwrap().as_str().unwrap(); + let timezone_string = + match m.get("timezone") { + Some(tz) => match tz.as_str() { + Some(tz_str) => tz_str, + None => return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }), + }, + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: + temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let zoned_date_time = ZonedDateTime::new( datetime_fixed_offset, Some(timezone_string.to_string()), ); return Ok(VariableValue::ZonedDateTime(zoned_date_time)); } - let truncated_date_time = - match truncated_naive_date_time.and_local_timezone(*offset) { - LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::InvalidType), - }; + let truncated_date_time = match truncated_naive_date_time + .and_local_timezone(*offset) + { + LocalResult::Single(dt) => dt, + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR + .to_string(), + }, + }) + } + }; let zoned_date_time = ZonedDateTime::new(truncated_date_time, timezone); Ok(VariableValue::ZonedDateTime(zoned_date_time)) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -869,9 +1325,9 @@ impl ScalarFunction for ClockFunction { async fn call( &self, context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { let timestamp = match self.clock { Clock::RealTime => context.get_realtime(), Clock::Statement => context.get_realtime(), @@ -888,19 +1344,37 @@ impl ScalarFunction for ClockFunction { } ClockResult::ZonedTime => VariableValue::ZonedTime(ZonedTime::new( zdt.datetime().time(), - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, )), ClockResult::ZonedDateTime => VariableValue::ZonedDateTime(zdt), }); } else { - let timezone_string = args[0].as_str().unwrap(); + let timezone_string = match &args[0] { + VariableValue::String(s) => s.as_str(), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } + }; let tz = match handle_iana_timezone(timezone_string).await { Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; let date_time = match tz.timestamp_millis_opt(timestamp as i64) { LocalResult::Single(dt) => dt.fixed_offset(), - _ => return Err(EvaluationError::ParseError), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } }; return Ok(match self.result { @@ -919,7 +1393,10 @@ impl ScalarFunction for ClockFunction { } } -async fn truncate_date(unit: String, date: NaiveDate) -> Result { +async fn truncate_date( + unit: String, + date: NaiveDate, +) -> Result { let year = date.year(); let month = date.month(); let day = date.day(); @@ -929,22 +1406,64 @@ async fn truncate_date(unit: String, date: NaiveDate) -> Result { let year = year / 1000 * 1000; - Ok(NaiveDate::from_ymd_opt(year, 1, 1).unwrap()) + Ok(match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }) } "century" => { let year = year / 100 * 100; - Ok(NaiveDate::from_ymd_opt(year, 1, 1).unwrap()) + Ok(match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }) } "decade" => { let year = year / 10 * 10; - Ok(NaiveDate::from_ymd_opt(year, 1, 1).unwrap()) + Ok(match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }) } - "year" => Ok(NaiveDate::from_ymd_opt(year, 1, 1).unwrap()), + "year" => Ok(match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }), "quarter" => { let month = month / 3 * 3 + 1; - Ok(NaiveDate::from_ymd_opt(year, month, 1).unwrap()) + Ok(match NaiveDate::from_ymd_opt(year, month, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }) } - "month" => Ok(NaiveDate::from_ymd_opt(year, month, 1).unwrap()), + "month" => Ok(match NaiveDate::from_ymd_opt(year, month, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }), "week" => { let weekday = date.weekday(); let days_to_subtract = match weekday { @@ -959,20 +1478,40 @@ async fn truncate_date(unit: String, date: NaiveDate) -> Result Ok(NaiveDate::from_ymd_opt(year, month, day).unwrap()), + "day" => Ok(match NaiveDate::from_ymd_opt(year, month, day) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }), "weekyear" => { // First day of the first week of the year let date_string = format!("{}-1-1", year); let date = match NaiveDate::parse_from_str(&date_string, "%Y-%W-%u") { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; Ok(date) } "hour" | "minute" | "second" | "millisecond" | "microsecond" => { - Ok(NaiveDate::from_ymd_opt(year, month, day).unwrap()) + Ok(match NaiveDate::from_ymd_opt(year, month, day) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionEvaluationError::InvalidType { + expected: "Valid truncation unit".to_string(), + }), } } @@ -980,25 +1519,37 @@ async fn truncate_date_with_map( unit: String, date: NaiveDate, map: BTreeMap, -) -> Result { +) -> Result { let year = date.year(); let month = date.month(); let day = date.day(); let years_to_add = match map.get("year") { - Some(year) => year.as_i64().unwrap() - 1, + Some(year) => match year.as_i64() { + Some(year) => year - 1, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let months_to_add = match map.get("month") { - Some(month) => month.as_i64().unwrap() - 1, + Some(month) => match month.as_i64() { + Some(month) => month - 1, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let days_to_add = match map.get("day") { - Some(day) => day.as_i64().unwrap() - 1, + Some(day) => match day.as_i64() { + Some(day) => day - 1, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let days_of_week_to_add = match map.get("dayOfWeek") { - Some(day_of_week) => day_of_week.as_i64().unwrap() - 1, + Some(day_of_week) => match day_of_week.as_i64() { + Some(day_of_week) => day_of_week - 1, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; @@ -1009,25 +1560,67 @@ async fn truncate_date_with_map( match truncation_unit { "millennium" => { let year = year / 1000 * 1000; - truncated_date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "century" => { let year = year / 100 * 100; - truncated_date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "decade" => { let year = year / 10 * 10; - truncated_date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "year" => { - truncated_date = NaiveDate::from_ymd_opt(year, 1, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, 1, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "quarter" => { let month = month / 3 * 3 + 1; - truncated_date = NaiveDate::from_ymd_opt(year, month, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, month, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "month" => { - truncated_date = NaiveDate::from_ymd_opt(year, month, 1).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, month, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "week" => { let weekday = date.weekday(); @@ -1044,31 +1637,57 @@ async fn truncate_date_with_map( truncated_date = date - chrono::Duration::days(days_to_subtract as i64); } "day" => { - truncated_date = NaiveDate::from_ymd_opt(year, month, day).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, month, day) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } "weekyear" => { // First day of the first week of the year let date_string = format!("{}-1-1", year); let date = match NaiveDate::parse_from_str(&date_string, "%Y-%W-%u") { Ok(date) => date, - Err(_) => return Err(EvaluationError::InvalidType), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; truncated_date = date; } "hour" | "minute" | "second" | "millisecond" | "microsecond" => { - truncated_date = NaiveDate::from_ymd_opt(year, month, day).unwrap(); + truncated_date = match NaiveDate::from_ymd_opt(year, month, day) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; } _ => { - return Err(EvaluationError::InvalidType); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } }; - truncated_date = NaiveDate::from_ymd_opt( + truncated_date = match NaiveDate::from_ymd_opt( truncated_date.year() + years_to_add as i32, truncated_date.month() + months_to_add as u32, truncated_date.day() + days_to_add as u32, - ) - .unwrap(); + ) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; if truncation_unit == "week" || truncation_unit == "weekyear" { truncated_date += chrono::Duration::days(days_of_week_to_add); } @@ -1076,7 +1695,10 @@ async fn truncate_date_with_map( Ok(truncated_date) } -async fn truncate_local_time(unit: String, time: NaiveTime) -> Result { +async fn truncate_local_time( + unit: String, + time: NaiveTime, +) -> Result { let hour = time.hour(); let minute = time.minute(); let second = time.second(); @@ -1084,35 +1706,76 @@ async fn truncate_local_time(unit: String, time: NaiveTime) -> Result Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "century" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "decade" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "year" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "month" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "week" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "quarter" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "weekyear" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "day" => Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()), - "hour" => Ok(NaiveTime::from_hms_opt(hour, 0, 0).unwrap()), - "minute" => Ok(NaiveTime::from_hms_opt(hour, minute, 0).unwrap()), - "second" => Ok(NaiveTime::from_hms_opt(hour, minute, second).unwrap()), + "millennium" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "century" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "decade" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "year" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "month" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "week" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "quarter" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "weekyear" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "day" => Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME), + "hour" => Ok(match NaiveTime::from_hms_opt(hour, 0, 0) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }), + "minute" => Ok(match NaiveTime::from_hms_opt(hour, minute, 0) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }), + "second" => Ok(match NaiveTime::from_hms_opt(hour, minute, second) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }), "millisecond" => { let divisor = 10u32.pow(6); let truncated_nanos = (nanosecond / divisor) * divisor; - Ok(NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos).unwrap()) + Ok( + match NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR + .to_string(), + }) + } + }, + ) } "microsecond" => { let divisor = 10u32.pow(3); let truncated_nanos = (nanosecond / divisor) * divisor; - Ok(NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos).unwrap()) + Ok( + match NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR + .to_string(), + }) + } + }, + ) } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionEvaluationError::InvalidType { + expected: "Valid truncation unit".to_string(), + }), } } @@ -1120,34 +1783,52 @@ async fn truncate_local_time_with_map( unit: String, time: NaiveTime, map: BTreeMap, -) -> Result { +) -> Result { let hour = time.hour(); let minute = time.minute(); let second = time.second(); let nanosecond = time.nanosecond(); let hour_to_add = match map.get("hour") { - Some(hour) => hour.as_i64().unwrap(), + Some(hour) => match hour.as_i64() { + Some(hour) => hour, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let minute_to_add = match map.get("minute") { - Some(minute) => minute.as_i64().unwrap(), + Some(minute) => match minute.as_i64() { + Some(minute) => minute, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let second_to_add = match map.get("second") { - Some(second) => second.as_i64().unwrap(), + Some(second) => match second.as_i64() { + Some(second) => second, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let milliseconds_to_add = match map.get("millisecond") { - Some(millisecond) => millisecond.as_i64().unwrap(), + Some(millisecond) => match millisecond.as_i64() { + Some(millisecond) => millisecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let microseconds_to_add = match map.get("microsecond") { - Some(microsecond) => microsecond.as_i64().unwrap(), + Some(microsecond) => match microsecond.as_i64() { + Some(microsecond) => microsecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; let nanoseconds_to_add = match map.get("nanosecond") { - Some(nanosecond) => nanosecond.as_i64().unwrap(), + Some(nanosecond) => match nanosecond.as_i64() { + Some(nanosecond) => nanosecond, + None => return Err(FunctionEvaluationError::OverflowError), + }, None => 0, }; @@ -1157,40 +1838,61 @@ async fn truncate_local_time_with_map( let mut truncated_time; match truncation_unit { "millennium" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "century" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "decade" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "weekyear" => { - return Ok(NaiveTime::from_hms_opt(0, 0, 0).unwrap()); + return Ok(*temporal_constants::MIDNIGHT_NAIVE_TIME); } "year" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "month" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "week" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "quarter" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "day" => { - truncated_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + truncated_time = *temporal_constants::MIDNIGHT_NAIVE_TIME; } "hour" => { - truncated_time = NaiveTime::from_hms_opt(hour, 0, 0).unwrap(); + truncated_time = match NaiveTime::from_hms_opt(hour, 0, 0) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; } "minute" => { - truncated_time = NaiveTime::from_hms_opt(hour, minute, 0).unwrap(); + truncated_time = match NaiveTime::from_hms_opt(hour, minute, 0) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; } "second" => { - truncated_time = NaiveTime::from_hms_opt(hour, minute, second).unwrap(); + truncated_time = match NaiveTime::from_hms_opt(hour, minute, second) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; } "millisecond" => { let divisor = 10u32.pow(6); @@ -1198,26 +1900,50 @@ async fn truncate_local_time_with_map( let truncated_nanos = (nanosecond / divisor) * divisor; truncated_time = - NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos).unwrap(); + match NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR + .to_string(), + }) + } + }; } "microsecond" => { let divisor = 10u32.pow(3); let truncated_nanos = (nanosecond / divisor) * divisor; truncated_time = - NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos).unwrap(); + match NaiveTime::from_hms_nano_opt(hour, minute, second, truncated_nanos) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR + .to_string(), + }) + } + }; } "timezone" => { // don't truncate timezone - truncated_time = - NaiveTime::from_hms_nano_opt(hour, minute, second, nanosecond).unwrap(); + truncated_time = match NaiveTime::from_hms_nano_opt(hour, minute, second, nanosecond) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; } _ => { - return Err(EvaluationError::InvalidType); + return Err(FunctionEvaluationError::InvalidType { + expected: "Valid truncation unit".to_string(), + }); } } - truncated_time = NaiveTime::from_hms_nano_opt( + truncated_time = match NaiveTime::from_hms_nano_opt( truncated_time.hour() + hour_to_add as u32, truncated_time.minute() + minute_to_add as u32, truncated_time.second() + second_to_add as u32, @@ -1225,8 +1951,14 @@ async fn truncate_local_time_with_map( + milliseconds_to_add as u32 * 1_000_000 + microseconds_to_add as u32 * 1_000 + nanoseconds_to_add as u32, - ) - .unwrap(); + ) { + Some(time) => time, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } + }; Ok(truncated_time) } @@ -1242,8 +1974,8 @@ async fn handle_iana_timezone(input: &str) -> Option { Some(tz) } -async fn parse_date_string(date_str: &str) -> Result { - let temp = date_str_formatter(date_str).await; +async fn parse_date_string(date_str: &str) -> Result { + let temp = date_str_formatter(date_str).await?; let input = temp.as_str(); // YYYYMMDD @@ -1255,7 +1987,9 @@ async fn parse_date_string(date_str: &str) -> Result return Ok(date); } - return Err(EvaluationError::ParseError); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } // YYYYDDD @@ -1265,10 +1999,12 @@ async fn parse_date_string(date_str: &str) -> Result return Ok(date); } } - Err(EvaluationError::ParseError) + Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) } -async fn date_str_formatter(input: &str) -> String { +async fn date_str_formatter(input: &str) -> Result { // Removes dash let temp = input.replace('-', ""); let date_str = temp.as_str(); @@ -1276,7 +2012,7 @@ async fn date_str_formatter(input: &str) -> String { // NaiveDate parser does not support date in the format of YYYYMM // Changing this to YYYYMM01 (as suggested by Neo4j) if date_str.len() == 6 && !date_str.contains('Q') && !date_str.contains('W') { - return format!("{}01", date_str); + return Ok(format!("{}01", date_str)); } // YYYYWwwD, YYYYWww @@ -1288,75 +2024,98 @@ async fn date_str_formatter(input: &str) -> String { // Subtract 1 from the week number parsed_week_number -= 1; if date_str.len() == 7 { - return format!( + return Ok(format!( "{}W{}{}1", &date_str[..week_index], parsed_week_number, &date_str[week_index + 3..] - ); + )); } // Create the modified string - return format!( + return Ok(format!( "{}W{}{}", &date_str[..week_index], parsed_week_number, &date_str[week_index + 3..] - ); + )); } } } if date_str.contains('Q') { // Attempt to parse as a quarter-based date - if let Ok(date) = parse_quarter_date(input).await { - return date.replace('-', ""); - } + let date = parse_quarter_date(input).await?; + return Ok(date); } // YYYY if date_str.len() == 4 { let formatted_input = format!("{}0101", date_str); - return formatted_input; + return Ok(formatted_input); } - date_str.to_string() + Ok(date_str.to_string()) } -async fn parse_quarter_date(date_str: &str) -> Result { +async fn parse_quarter_date(date_str: &str) -> Result { let parts: Vec<&str> = date_str.split('-').collect(); if parts.len() < 2 { - return Err(EvaluationError::ParseError); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } let year = match parts[0].parse::() { Ok(y) => y, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; let quarter = match parts[1].chars().nth(1) { Some(q) => { if q.is_ascii_digit() { match q.to_digit(10) { Some(q) => q as i32, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } } } else { - return Err(EvaluationError::ParseError); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } } - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; if !(1..=4).contains(&quarter) { - return Err(EvaluationError::ParseError); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } let day_of_quarter = match parts[2].parse::() { Ok(d) => d, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; if !(1..=92).contains(&day_of_quarter) { - return Err(EvaluationError::ParseError); + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }); } let month = match quarter { @@ -1367,25 +2126,40 @@ async fn parse_quarter_date(date_str: &str) -> Result { _ => unreachable!(), }; - let temp_date = NaiveDate::from_ymd_opt(year, month, 1).unwrap(); + let temp_date = match NaiveDate::from_ymd_opt(year, month, 1) { + Some(date) => date, + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } + }; let date = match temp_date.checked_add_days(Days::new(day_of_quarter as u64 - 1)) { Some(d) => d, - None => return Err(EvaluationError::OutOfRange), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_DATE_FORMAT_ERROR.to_string(), + }) + } }; let date_format = date.to_string(); Ok(date_format) } -async fn parse_local_time_input(input: &str) -> Result { +async fn parse_local_time_input(input: &str) -> Result { let mut input_string = input.to_string().replace(':', ""); let contains_fractional_seconds = input_string.contains('.'); if contains_fractional_seconds { let time = match NaiveTime::parse_from_str(&input_string, "%H%M%S%.f") { Ok(t) => t, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; return Ok(time); } @@ -1398,45 +2172,46 @@ async fn parse_local_time_input(input: &str) -> Result t, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; Ok(time) } -async fn parse_local_date_time_input(input: &str) -> Result { +async fn parse_local_date_time_input( + input: &str, +) -> Result { let input_string = input.to_string().replace([':', '-'], ""); let date_string = match input_string.split('T').next() { Some(date) => date, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let time_string = match input_string.split('T').last() { Some(time) => time, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; - let naive_date; - let naive_time; - - if let Ok(date) = parse_date_string(date_string).await { - naive_date = date; - } else { - println!("Failed to parse the date string"); - return Err(EvaluationError::ParseError); - } + let naive_date = parse_date_string(date_string).await?; - if let Ok(time) = parse_local_time_input(time_string).await { - naive_time = time; - } else { - println!("Failed to parse the time string"); - return Err(EvaluationError::ParseError); - } + let naive_time = parse_local_time_input(time_string).await?; Ok(NaiveDateTime::new(naive_date, naive_time)) } -async fn parse_zoned_time_input(input: &str) -> Result { +async fn parse_zoned_time_input(input: &str) -> Result { let input_string = input.to_string().replace(':', ""); let contains_frac = input.contains('.'); let is_utc = input.contains('Z'); @@ -1446,48 +2221,65 @@ async fn parse_zoned_time_input(input: &str) -> Result time, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; let timezone = match extract_timezone(&input_string).await { Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; if is_utc { - let offset = FixedOffset::east_opt(0).unwrap(); + let offset = *temporal_constants::UTC_FIXED_OFFSET; if contains_frac { let naive_time = match NaiveTime::parse_from_str(time_string, "%H%M%S%.f") { Ok(t) => t, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; return Ok(ZonedTime::new(naive_time, offset)); } let naive_time = match NaiveTime::parse_from_str(time_string, "%H%M%S") { Ok(t) => t, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; return Ok(ZonedTime::new(naive_time, offset)); } - let naive_time; - - if let Ok(time) = parse_local_time_input(time_string).await { - naive_time = time; - } else { - println!("Failed to parse the time string"); - return Err(EvaluationError::ParseError); - } + let naive_time = parse_local_time_input(time_string).await?; let offset = match FixedOffset::from_str(timezone) { Ok(o) => o, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; if contains_frac { let naive_time = match NaiveTime::parse_from_str(time_string, "%H%M%S%.f") { Ok(t) => t, - Err(_) => return Err(EvaluationError::ParseError), + Err(_) => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_LOCAL_TIME_FORMAT_ERROR.to_string(), + }) + } }; let zoned_time = ZonedTime::new(naive_time, offset); return Ok(zoned_time); @@ -1498,7 +2290,10 @@ async fn parse_zoned_time_input(input: &str) -> Result Option<&str> { - let re = Regex::new(r"(\d{2,6}(\.\d{1,9})?)(([+-].*)|Z|(\[.*?\])?)?").unwrap(); + let re = match Regex::new(r"(\d{2,6}(\.\d{1,9})?)(([+-].*)|Z|(\[.*?\])?)?") { + Ok(re) => re, + Err(_) => return None, + }; if let Some(captures) = re.captures(input) { captures.get(3).map(|m| m.as_str()) @@ -1507,18 +2302,28 @@ async fn extract_timezone(input: &str) -> Option<&str> { } } -async fn parse_zoned_date_time_input(input: &str) -> Result { +async fn parse_zoned_date_time_input( + input: &str, +) -> Result { let is_utc = input.contains('Z'); let date_part = match input.split('T').next() { Some(date) => date.replace('-', ""), - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let date_string = date_part.as_str(); let timezone_part = match input.split('T').last() { Some(timezone) => timezone.replace(':', ""), - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let timezone_string = timezone_part.as_str(); @@ -1527,38 +2332,36 @@ async fn parse_zoned_date_time_input(input: &str) -> Result time, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let timezone = match extract_timezone(timezone_string).await { Some(tz) => tz, - None => return Err(EvaluationError::ParseError), + None => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; - let naive_date; - let naive_time; - - if let Ok(date) = parse_date_string(date_string).await { - naive_date = date; - } else { - println!("Failed to parse the date string"); - return Err(EvaluationError::ParseError); - } - - if let Ok(time) = parse_local_time_input(time_string).await { - naive_time = time; - } else { - println!("Failed to parse the time string"); - return Err(EvaluationError::ParseError); - } + let naive_date = parse_date_string(date_string).await?; + let naive_time = parse_local_time_input(time_string).await?; let naive_date_time = NaiveDateTime::new(naive_date, naive_time); if is_utc { let date_time = match NaiveDateTime::and_local_timezone( &naive_date_time, - FixedOffset::east_opt(0).unwrap(), + *temporal_constants::UTC_FIXED_OFFSET, ) { LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::ParseError), + _ => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let zoned_date_time = ZonedDateTime::new(date_time, None); return Ok(zoned_date_time); @@ -1567,11 +2370,19 @@ async fn parse_zoned_date_time_input(input: &str) -> Result t, - _err => return Err(EvaluationError::ParseError), + _err => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let date_time = match NaiveDateTime::and_local_timezone(&naive_date_time, tz) { LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::ParseError), + _ => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let date_time_offset = date_time.fixed_offset(); let zoned_date_time = ZonedDateTime::new(date_time_offset, Some(timezone.to_string())); @@ -1581,15 +2392,18 @@ async fn parse_zoned_date_time_input(input: &str) -> Result o, Err(_) => { - return Err(EvaluationError::FunctionError { - function_name: "parse_zoned_date_time_input".to_string(), - error: Box::new(EvaluationError::ParseError), - }) + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }); } }; let date_time = match NaiveDateTime::and_local_timezone(&naive_date_time, offset) { LocalResult::Single(dt) => dt, - _ => return Err(EvaluationError::ParseError), + _ => { + return Err(FunctionEvaluationError::InvalidFormat { + expected: temporal_constants::INVALID_ZONED_DATETIME_FORMAT_ERROR.to_string(), + }) + } }; let zoned_date_time = ZonedDateTime::new(date_time, None); Ok(zoned_date_time) diff --git a/core/src/evaluation/functions/text/tests/left_tests.rs b/core/src/evaluation/functions/text/tests/left_tests.rs index da197b0..c75b246 100644 --- a/core/src/evaluation/functions/text/tests/left_tests.rs +++ b/core/src/evaluation/functions/text/tests/left_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -50,7 +52,13 @@ async fn test_left_invalid_inputs() { VariableValue::String("drasi".to_string()), ]; let result = left.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1) + } + )); } #[tokio::test] @@ -65,7 +73,14 @@ async fn test_left_invalid_input_value() { VariableValue::Integer((-1).into()), ]; let result = left.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + let error = result.unwrap_err(); + assert!(matches!( + error, + FunctionError { + function_name: _, + error: FunctionEvaluationError::OverflowError + } + )); } #[tokio::test] @@ -83,7 +98,10 @@ async fn test_left_too_many_args() { let result = left.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -98,14 +116,20 @@ async fn test_left_too_few_args() { let result = left.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); let args = vec![]; let result = left.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -130,5 +154,11 @@ async fn test_left_null() { ]; let result = left.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1) + } + )); } diff --git a/core/src/evaluation/functions/text/tests/replace_tests.rs b/core/src/evaluation/functions/text/tests/replace_tests.rs index 2eb88a9..a507093 100644 --- a/core/src/evaluation/functions/text/tests/replace_tests.rs +++ b/core/src/evaluation/functions/text/tests/replace_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -86,7 +88,13 @@ async fn test_replace_invalid_inputs() { VariableValue::Integer(123.into()), ]; let result = replace.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(2), + } + )); } #[tokio::test] @@ -105,7 +113,10 @@ async fn test_replace_too_many_args() { let result = replace.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -123,7 +134,10 @@ async fn test_replace_too_few_args() { let result = replace.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } diff --git a/core/src/evaluation/functions/text/tests/reverse_tests.rs b/core/src/evaluation/functions/text/tests/reverse_tests.rs index bd37a1c..fcb488c 100644 --- a/core/src/evaluation/functions/text/tests/reverse_tests.rs +++ b/core/src/evaluation/functions/text/tests/reverse_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -41,7 +43,13 @@ async fn test_reverse_invalid_inputs() { let args = vec![VariableValue::Integer(10.into())]; let result = reverse.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0), + } + )); } #[tokio::test] @@ -58,7 +66,10 @@ async fn test_reverse_too_many_args() { let result = reverse.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } diff --git a/core/src/evaluation/functions/text/tests/right_tests.rs b/core/src/evaluation/functions/text/tests/right_tests.rs index 9dc236e..6b160b3 100644 --- a/core/src/evaluation/functions/text/tests/right_tests.rs +++ b/core/src/evaluation/functions/text/tests/right_tests.rs @@ -7,7 +7,9 @@ use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::float::Float; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -54,7 +56,10 @@ async fn test_right_too_many_args() { let result = right.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -69,7 +74,10 @@ async fn test_right_too_few_args() { let result = right.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -85,14 +93,28 @@ async fn test_right_invalid_input() { VariableValue::Float(Float::from_f64(3.0).unwrap()), ]; let result = right.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + let err = result.unwrap_err(); + assert!(matches!( + err, + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1) + } + )); let args = vec![ VariableValue::String("drasi".to_string()), VariableValue::String("WORLD".to_string()), ]; let result = right.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + let err = result.unwrap_err(); + assert!(matches!( + err, + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); let args = vec![ VariableValue::String("drasi".to_string()), @@ -123,5 +145,11 @@ async fn test_right_null() { ]; let result = right.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1) + } + )); } diff --git a/core/src/evaluation/functions/text/tests/split_tests.rs b/core/src/evaluation/functions/text/tests/split_tests.rs index 6917151..da2dd69 100644 --- a/core/src/evaluation/functions/text/tests/split_tests.rs +++ b/core/src/evaluation/functions/text/tests/split_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -78,7 +80,13 @@ async fn test_split_invalid_inputs() { ]), ]; let result = split.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); } #[tokio::test] @@ -96,7 +104,10 @@ async fn test_split_too_many_args() { let result = split.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -111,7 +122,10 @@ async fn test_split_too_few_args() { let result = split.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -185,7 +199,13 @@ async fn test_split_multiple_delimiter_invalid_inputs() { ]), ]; let result = split.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); } #[tokio::test] diff --git a/core/src/evaluation/functions/text/tests/substring_tests.rs b/core/src/evaluation/functions/text/tests/substring_tests.rs index 8bd9c26..1cf109c 100644 --- a/core/src/evaluation/functions/text/tests/substring_tests.rs +++ b/core/src/evaluation/functions/text/tests/substring_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -87,7 +89,10 @@ async fn test_substring_too_many_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -104,7 +109,10 @@ async fn test_substring_too_few_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -122,7 +130,13 @@ async fn test_substring_invalid_input_values() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); let args = vec![ VariableValue::String("drasiReactivegraph".to_string()), @@ -132,7 +146,13 @@ async fn test_substring_invalid_input_values() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidType { expected: _ }, + } + )); let args = vec![ VariableValue::String("drasiReactivegraph".to_string()), @@ -142,7 +162,14 @@ async fn test_substring_invalid_input_values() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + // Negative start index + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidType { expected: _ }, + } + )); let args = vec![ VariableValue::String("drasiReactivegraph".to_string()), @@ -152,7 +179,13 @@ async fn test_substring_invalid_input_values() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidType { expected: _ }, + } + )); } #[tokio::test] @@ -169,7 +202,13 @@ async fn test_substring_invalid_inputs() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); } #[tokio::test] @@ -192,5 +231,11 @@ async fn test_substring_null() { let result = substring .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(1), + } + )); } diff --git a/core/src/evaluation/functions/text/tests/to_lower_tests.rs b/core/src/evaluation/functions/text/tests/to_lower_tests.rs index bd6b59d..dc52291 100644 --- a/core/src/evaluation/functions/text/tests/to_lower_tests.rs +++ b/core/src/evaluation/functions/text/tests/to_lower_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -46,7 +48,10 @@ async fn test_to_lower_too_many_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -63,7 +68,10 @@ async fn test_to_lower_too_few_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -78,7 +86,13 @@ async fn test_to_lower_invalid_args() { let result = to_lower .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0) + } + )); } #[tokio::test] diff --git a/core/src/evaluation/functions/text/tests/to_string_or_null_tests.rs b/core/src/evaluation/functions/text/tests/to_string_or_null_tests.rs index 9f41163..20dcf26 100644 --- a/core/src/evaluation/functions/text/tests/to_string_or_null_tests.rs +++ b/core/src/evaluation/functions/text/tests/to_string_or_null_tests.rs @@ -1,118 +1,126 @@ -use drasi_query_ast::ast; - -use super::text; -use std::collections::BTreeMap; -use std::sync::Arc; - -use crate::evaluation::context::QueryVariables; -use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; - -fn get_func_expr() -> ast::FunctionExpression { - ast::FunctionExpression { - name: Arc::from("function"), - args: vec![], - position_in_query: 10, - } -} - -#[tokio::test] -async fn test_to_string_null() { - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::Integer(123.into())]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert_eq!(result.unwrap(), VariableValue::String("123".to_string())); - - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![VariableValue::List(vec![ - VariableValue::Integer(1.into()), - VariableValue::Integer(2.into()), - VariableValue::Integer(3.into()), - ])]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert_eq!( - result.unwrap(), - VariableValue::String("[1, 2, 3]".to_string()) - ); - - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - let map: BTreeMap = BTreeMap::from([ - ( - "key1".to_string(), - VariableValue::String("value1".to_string()), - ), - ( - "foo2".to_string(), - VariableValue::String("bar2".to_string()), - ), - ]) - .clone(); - let args = vec![VariableValue::Object(map)]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert_eq!(result.unwrap(), VariableValue::Null); - - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - let args = vec![VariableValue::Null]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert_eq!(result.unwrap(), VariableValue::Null); -} - -#[tokio::test] -async fn test_to_string_null_too_many_args() { - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![ - VariableValue::Integer(123.into()), - VariableValue::Integer(123.into()), - ]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} - -#[tokio::test] -async fn test_to_string_null_too_few_args() { - let to_string = text::ToStringOrNull {}; - let binding = QueryVariables::new(); - let context = - ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); - - let args = vec![]; - let result = to_string - .call(&context, &get_func_expr(), args.clone()) - .await; - assert!(matches!( - result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) - )); -} +use drasi_query_ast::ast; + +use super::text; +use std::collections::BTreeMap; +use std::sync::Arc; + +use crate::evaluation::context::QueryVariables; +use crate::evaluation::functions::ScalarFunction; +use crate::evaluation::variable_value::VariableValue; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; + +fn get_func_expr() -> ast::FunctionExpression { + ast::FunctionExpression { + name: Arc::from("function"), + args: vec![], + position_in_query: 10, + } +} + +#[tokio::test] +async fn test_to_string_null() { + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::Integer(123.into())]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert_eq!(result.unwrap(), VariableValue::String("123".to_string())); + + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![VariableValue::List(vec![ + VariableValue::Integer(1.into()), + VariableValue::Integer(2.into()), + VariableValue::Integer(3.into()), + ])]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert_eq!( + result.unwrap(), + VariableValue::String("[1, 2, 3]".to_string()) + ); + + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + let map: BTreeMap = BTreeMap::from([ + ( + "key1".to_string(), + VariableValue::String("value1".to_string()), + ), + ( + "foo2".to_string(), + VariableValue::String("bar2".to_string()), + ), + ]) + .clone(); + let args = vec![VariableValue::Object(map)]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert_eq!(result.unwrap(), VariableValue::Null); + + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + let args = vec![VariableValue::Null]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert_eq!(result.unwrap(), VariableValue::Null); +} + +#[tokio::test] +async fn test_to_string_null_too_many_args() { + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![ + VariableValue::Integer(123.into()), + VariableValue::Integer(123.into()), + ]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} + +#[tokio::test] +async fn test_to_string_null_too_few_args() { + let to_string = text::ToStringOrNull {}; + let binding = QueryVariables::new(); + let context = + ExpressionEvaluationContext::new(&binding, Arc::new(InstantQueryClock::new(0, 0))); + + let args = vec![]; + let result = to_string + .call(&context, &get_func_expr(), args.clone()) + .await; + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } + )); +} diff --git a/core/src/evaluation/functions/text/tests/to_string_tests.rs b/core/src/evaluation/functions/text/tests/to_string_tests.rs index e8b1e4d..7d564c1 100644 --- a/core/src/evaluation/functions/text/tests/to_string_tests.rs +++ b/core/src/evaluation/functions/text/tests/to_string_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -64,7 +66,10 @@ async fn test_to_string_too_many_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -81,7 +86,10 @@ async fn test_to_string_too_few_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } diff --git a/core/src/evaluation/functions/text/tests/to_upper_tests.rs b/core/src/evaluation/functions/text/tests/to_upper_tests.rs index 557ea5b..2c53c39 100644 --- a/core/src/evaluation/functions/text/tests/to_upper_tests.rs +++ b/core/src/evaluation/functions/text/tests/to_upper_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -46,7 +48,10 @@ async fn test_to_upper_too_many_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -63,7 +68,10 @@ async fn test_to_upper_too_few_args() { .await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -78,7 +86,13 @@ async fn test_to_upper_invalid_args() { let result = to_upper .call(&context, &get_func_expr(), args.clone()) .await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0) + } + )); } #[tokio::test] diff --git a/core/src/evaluation/functions/text/tests/trim_tests.rs b/core/src/evaluation/functions/text/tests/trim_tests.rs index 7135d79..631aea5 100644 --- a/core/src/evaluation/functions/text/tests/trim_tests.rs +++ b/core/src/evaluation/functions/text/tests/trim_tests.rs @@ -6,7 +6,9 @@ use super::text; use crate::evaluation::context::QueryVariables; use crate::evaluation::functions::ScalarFunction; use crate::evaluation::variable_value::VariableValue; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext, InstantQueryClock}; +use crate::evaluation::{ + ExpressionEvaluationContext, FunctionError, FunctionEvaluationError, InstantQueryClock, +}; fn get_func_expr() -> ast::FunctionExpression { ast::FunctionExpression { @@ -65,7 +67,10 @@ async fn test_trim_too_many_args() { let result = trim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); let args = vec![ @@ -75,7 +80,10 @@ async fn test_trim_too_many_args() { let result = ltrim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); let args = vec![ @@ -85,7 +93,10 @@ async fn test_trim_too_many_args() { let result = rtrim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount + } )); } @@ -98,7 +109,13 @@ async fn test_trim_invalid_inputs() { let args = vec![VariableValue::Integer(123.into())]; let result = trim.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0), + } + )); let ltrim = text::LTrim {}; let binding = QueryVariables::new(); @@ -107,7 +124,13 @@ async fn test_trim_invalid_inputs() { let args = vec![VariableValue::Integer(123.into())]; let result = ltrim.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0), + } + )); let rtrim = text::RTrim {}; let binding = QueryVariables::new(); @@ -116,7 +139,13 @@ async fn test_trim_invalid_inputs() { let args = vec![VariableValue::Integer(123.into())]; let result = rtrim.call(&context, &get_func_expr(), args.clone()).await; - assert!(matches!(result.unwrap_err(), EvaluationError::InvalidType)); + assert!(matches!( + result.unwrap_err(), + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgument(0), + } + )); } #[tokio::test] @@ -132,21 +161,30 @@ async fn test_trim_too_few_args() { let result = trim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount, + } )); let args = vec![]; let result = ltrim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount, + } )); let args = vec![]; let result = rtrim.call(&context, &get_func_expr(), args.clone()).await; assert!(matches!( result.unwrap_err(), - EvaluationError::InvalidArgumentCount(_) + FunctionError { + function_name: _, + error: FunctionEvaluationError::InvalidArgumentCount, + } )); } diff --git a/core/src/evaluation/functions/text/text.rs b/core/src/evaluation/functions/text/text.rs index 7afcc10..8e98cf5 100644 --- a/core/src/evaluation/functions/text/text.rs +++ b/core/src/evaluation/functions/text/text.rs @@ -3,7 +3,7 @@ use async_trait::async_trait; use drasi_query_ast::ast; use crate::evaluation::functions::ScalarFunction; -use crate::evaluation::{EvaluationError, ExpressionEvaluationContext}; +use crate::evaluation::{ExpressionEvaluationContext, FunctionError, FunctionEvaluationError}; #[derive(Debug, PartialEq)] pub struct ToUpper {} @@ -13,16 +13,22 @@ impl ScalarFunction for ToUpper { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("toUpper".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.to_uppercase())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -35,16 +41,22 @@ impl ScalarFunction for ToLower { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("toLower".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.to_lowercase())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -57,16 +69,22 @@ impl ScalarFunction for Trim { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("trim".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.trim().to_string())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -79,16 +97,22 @@ impl ScalarFunction for LTrim { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("ltrim".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.trim_start().to_string())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -101,16 +125,22 @@ impl ScalarFunction for RTrim { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("rtrim".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.trim_end().to_string())), VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -123,11 +153,14 @@ impl ScalarFunction for Reverse { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount("reverse".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::String(s) => Ok(VariableValue::String(s.chars().rev().collect())), @@ -137,7 +170,10 @@ impl ScalarFunction for Reverse { Ok(VariableValue::List(l)) } VariableValue::Null => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -150,11 +186,14 @@ impl ScalarFunction for Left { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("left".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1]) { (VariableValue::Null, VariableValue::Integer(_length)) => Ok(VariableValue::Null), @@ -163,11 +202,19 @@ impl ScalarFunction for Left { let len = match length.as_i64() { Some(l) => { if l <= 0 { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }); } l as usize } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; if len > original.len() { return Ok(VariableValue::String(original.to_string())); @@ -175,8 +222,16 @@ impl ScalarFunction for Left { let result = original.chars().take(len).collect::(); Ok(VariableValue::String(result)) } - (_, VariableValue::Null) => return Err(EvaluationError::InvalidType), - _ => Err(EvaluationError::InvalidType), + (VariableValue::String(_original), _) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -189,11 +244,14 @@ impl ScalarFunction for Right { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("right".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1]) { (VariableValue::Null, VariableValue::Integer(_length)) => Ok(VariableValue::Null), @@ -202,11 +260,19 @@ impl ScalarFunction for Right { let len = match length.as_i64() { Some(l) => { if l <= 0 { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::OverflowError, + }); } l as usize } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; if len > original.len() { return Ok(VariableValue::String(original.to_string())); @@ -215,8 +281,16 @@ impl ScalarFunction for Right { let result = original[start_index..].to_string(); Ok(VariableValue::String(result)) } - (_, VariableValue::Null) => return Err(EvaluationError::InvalidType), - _ => Err(EvaluationError::InvalidType), + (VariableValue::String(_), _) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -229,11 +303,14 @@ impl ScalarFunction for Replace { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 3 { - return Err(EvaluationError::InvalidArgumentCount("replace".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1], &args[2]) { ( @@ -247,10 +324,22 @@ impl ScalarFunction for Replace { let result = original.replace(search, replace); return Ok(VariableValue::String(result)); } + (VariableValue::Null, _, _) => Ok(VariableValue::Null), (_, VariableValue::Null, _) => Ok(VariableValue::Null), (_, _, VariableValue::Null) => Ok(VariableValue::Null), - _ => Err(EvaluationError::InvalidType), + (VariableValue::String(_), VariableValue::String(_), _) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }), + (_, VariableValue::String(_), _) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -263,18 +352,24 @@ impl ScalarFunction for Split { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 2 { - return Err(EvaluationError::InvalidArgumentCount("split".to_string())); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1]) { (VariableValue::Null, _) => Ok(VariableValue::Null), (_, VariableValue::Null) => Ok(VariableValue::Null), (VariableValue::String(original), VariableValue::String(separator)) => { if separator.is_empty() { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }); } let result = original .split(separator) @@ -291,7 +386,12 @@ impl ScalarFunction for Split { for delimiter in delimiters { match delimiter { VariableValue::String(s) => delimiters_vector.push(s), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } } } for c in original.chars() { @@ -309,7 +409,10 @@ impl ScalarFunction for Split { } return Ok(VariableValue::List(result)); } - _ => Err(EvaluationError::InvalidType), + _ => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }), } } } @@ -322,74 +425,129 @@ impl ScalarFunction for Substring { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() < 2 || args.len() > 3 { - return Err(EvaluationError::InvalidArgumentCount( - "substring".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match (&args[0], &args[1], &args.get(2)) { (VariableValue::Null, _, _) => Ok(VariableValue::Null), - (_, VariableValue::Null, _) => Err(EvaluationError::InvalidType), - (_, _, Some(VariableValue::Null)) => Err(EvaluationError::InvalidType), + (_, VariableValue::Null, _) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }), + (_, _, Some(VariableValue::Null)) => Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }), (VariableValue::String(original), VariableValue::Integer(start), None) => { // Handle case with two arguments - if !start.is_i64() { - return Err(EvaluationError::InvalidType); - } let start_index = match start.as_i64() { Some(s) => { if s < 0 { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "positive integer".to_string(), + }, + }); } s as usize } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; if start_index > original.len() { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }); } return Ok(VariableValue::String(original[start_index..].to_string())); } + (VariableValue::String(_), _, None) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }); + } ( VariableValue::String(original), VariableValue::Integer(start), Some(VariableValue::Integer(length)), ) => { - // Handle cases with three arguments - if !start.is_i64() || !length.is_i64() { - return Err(EvaluationError::InvalidType); - } let start_index = match start.as_i64() { Some(s) => { if s < 0 { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "positive integer".to_string(), + }, + }); } s as usize } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(1), + }) + } }; let len = match length.as_i64() { Some(l) => { if l < 0 { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "positive integer".to_string(), + }, + }); } l as usize } - None => return Err(EvaluationError::InvalidType), + None => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }) + } }; if start_index > original.len() || start_index + len - start_index > original.len() { - return Err(EvaluationError::InvalidType); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidType { + expected: "Valid index or length".to_string(), + }, + }); } return Ok(VariableValue::String( original[start_index..start_index + len].to_string(), )); } - _ => return Err(EvaluationError::InvalidType), + (VariableValue::String(_), VariableValue::Integer(_), _) => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(2), + }); + } + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } } } } @@ -401,13 +559,14 @@ impl ScalarFunction for ToString { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "to_string".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Integer(n) => return Ok(VariableValue::String(n.to_string())), @@ -426,7 +585,12 @@ impl ScalarFunction for ToString { return Ok(VariableValue::String(result)); } VariableValue::Null => return Ok(VariableValue::Null), - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } } } } @@ -438,13 +602,14 @@ impl ScalarFunction for ToStringOrNull { async fn call( &self, _context: &ExpressionEvaluationContext, - _expression: &ast::FunctionExpression, + expression: &ast::FunctionExpression, args: Vec, - ) -> Result { + ) -> Result { if args.len() != 1 { - return Err(EvaluationError::InvalidArgumentCount( - "to_string_or_null".to_string(), - )); + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgumentCount, + }); } match &args[0] { VariableValue::Null => return Ok(VariableValue::Null), @@ -466,7 +631,12 @@ impl ScalarFunction for ToStringOrNull { // For objects should return null return Ok(VariableValue::Null); } - _ => return Err(EvaluationError::InvalidType), + _ => { + return Err(FunctionError { + function_name: expression.name.to_string(), + error: FunctionEvaluationError::InvalidArgument(0), + }) + } } } } diff --git a/core/src/evaluation/mod.rs b/core/src/evaluation/mod.rs index f675b4f..bf9365b 100644 --- a/core/src/evaluation/mod.rs +++ b/core/src/evaluation/mod.rs @@ -7,7 +7,10 @@ pub mod parts; pub mod temporal_constants; pub mod variable_value; -use std::{error::Error, fmt::Display}; +use std::{ + error::Error, + fmt::{self, Display}, +}; pub use context::ExpressionEvaluationContext; pub use expressions::*; @@ -19,21 +22,113 @@ use crate::interface::{IndexError, MiddlewareError}; #[derive(Debug)] pub enum EvaluationError { DivideByZero, - InvalidType, + InvalidType { expected: String }, UnknownIdentifier(String), - UnknownFunction(String), - InvalidArgumentCount(String), + UnknownFunction(String), // Unknown Cypher function IndexError(IndexError), MiddlewareError(MiddlewareError), ParseError, InvalidContext, - OutOfRange, - FunctionError { - function_name: String, - error: Box, - }, - InvalidState, + OutOfRange { kind: OutOfRangeType }, + OverflowError, + FunctionError(FunctionError), + CorruptData, InvalidArgument, + UnknownProperty { property_name: String }, +} + +#[derive(Debug)] +pub struct FunctionError { + function_name: String, + error: FunctionEvaluationError, +} + +impl PartialEq for FunctionError { + fn eq(&self, other: &Self) -> bool { + self.error == other.error + } +} + +#[derive(Debug)] +pub enum FunctionEvaluationError { + InvalidArgument(usize), + InvalidArgumentCount, + IndexError(IndexError), + OverflowError, + OutofRange, + InvalidFormat { expected: String }, + CorruptData, + InvalidType { expected: String }, +} + +impl PartialEq for FunctionEvaluationError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + FunctionEvaluationError::InvalidArgument(a), + FunctionEvaluationError::InvalidArgument(b), + ) => a == b, + ( + FunctionEvaluationError::InvalidArgumentCount, + FunctionEvaluationError::InvalidArgumentCount, + ) => true, + (FunctionEvaluationError::IndexError(a), FunctionEvaluationError::IndexError(b)) => { + a == b + } + (FunctionEvaluationError::OverflowError, FunctionEvaluationError::OverflowError) => { + true + } + (FunctionEvaluationError::OutofRange, FunctionEvaluationError::OutofRange) => true, + ( + FunctionEvaluationError::InvalidFormat { .. }, + FunctionEvaluationError::InvalidFormat { .. }, + ) => true, + (FunctionEvaluationError::CorruptData, FunctionEvaluationError::CorruptData) => true, + ( + FunctionEvaluationError::InvalidType { .. }, + FunctionEvaluationError::InvalidType { .. }, + ) => true, + _ => false, + } + } +} + +impl fmt::Display for FunctionEvaluationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + FunctionEvaluationError::InvalidArgument(arg) => write!(f, "Invalid argument: {}", arg), + FunctionEvaluationError::InvalidArgumentCount => write!(f, "Invalid argument count"), + FunctionEvaluationError::IndexError(err) => write!(f, "Index error: {}", err), + FunctionEvaluationError::OverflowError => write!(f, "Overflow error"), + FunctionEvaluationError::OutofRange => write!(f, "Out of range"), + FunctionEvaluationError::InvalidFormat { expected } => { + write!(f, "Invalid format, expected: {}", expected) + } + FunctionEvaluationError::CorruptData => write!(f, "Invalid accumulator"), + FunctionEvaluationError::InvalidType { expected } => { + write!(f, "Invalid type, expected: {}", expected) + } + } + } +} + +impl fmt::Display for FunctionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Function: {}, Error: {}", self.function_name, self.error) + } +} + +#[derive(Debug)] +pub enum OutOfRangeType { + IndexOutOfRange, + TemporalDurationOutOfRange, + TemporalInstantOutOfRange, +} + +impl From for FunctionEvaluationError { + fn from(e: IndexError) -> Self { + FunctionEvaluationError::IndexError(e) + } } impl From for EvaluationError { @@ -49,6 +144,7 @@ impl From for EvaluationError { } impl Display for EvaluationError { + // Match the variant and handle the display differently for each variant fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { format!("{:?}", self).fmt(f) } diff --git a/core/src/evaluation/temporal_constants.rs b/core/src/evaluation/temporal_constants.rs index f52d56b..d65f065 100644 --- a/core/src/evaluation/temporal_constants.rs +++ b/core/src/evaluation/temporal_constants.rs @@ -2,11 +2,36 @@ use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; use lazy_static::lazy_static; lazy_static! { - pub static ref EPOCH_NAIVE_DATE: NaiveDate = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(); - pub static ref MIDNIGHT_NAIVE_TIME: NaiveTime = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + pub static ref EPOCH_NAIVE_DATE: NaiveDate = match NaiveDate::from_ymd_opt(1970, 1, 1) { + Some(date) => date, + None => unreachable!(), + }; + pub static ref MIDNIGHT_NAIVE_TIME: NaiveTime = match NaiveTime::from_hms_opt(0, 0, 0) { + Some(time) => time, + None => unreachable!(), + }; pub static ref EPOCH_MIDNIGHT_NAIVE_DATETIME: NaiveDateTime = - NaiveDate::from_ymd_opt(1970, 1, 1) - .unwrap() - .and_hms_opt(0, 0, 0) - .unwrap(); + NaiveDateTime::new(*EPOCH_NAIVE_DATE, *MIDNIGHT_NAIVE_TIME); + pub static ref UTC_FIXED_OFFSET: chrono::FixedOffset = match chrono::FixedOffset::east_opt(0) { + Some(offset) => offset, + None => unreachable!(), + }; } + +pub const INVALID_LOCAL_DATETIME_FORMAT_ERROR: &str = "A valid string representation of a local datetime or a map of local datetime components.\n\ + Examples include: localdatetime('2015-07-21T21:40:32.142'), localdatetime('2015202T21'), localdatetime({year: 1984, week: 10, dayOfWeek: 3,hour: 12, minute: 31, second: 14, millisecond: 645})"; + +pub const INVALID_ZONED_TIME_FORMAT_ERROR: &str = "A valid string representation of a zoned time or a map of zoned time components. \n\ + Examples include: time('214032.142Z'), time('21:40:32+01:00'), time('214032-0100'), time({hour: 12, minute: 31, second: 14, microsecond: 645876, timezone: '+01:00'})"; + +pub const INVALID_ZONED_DATETIME_FORMAT_ERROR: &str = "A valid string representation of a zoned datetime or a map of zoned datetime components.\n\ + Examples include: datetime('20150721T21:40-01:30'), datetime('2015-07-21T21:40:32.142+0100'), datetime({year: 1984, ordinalDay: 202, hour: 12, minute: 31, second: 14, timezone: '+01:00'})"; + +pub const INVALID_LOCAL_TIME_FORMAT_ERROR: &str = "A valid string representation of a local time or a map of local time components.\n\ + Examples include: localtime('21:40:32.142'), localtime('214032.142'), localtime({hour: 12, minute: 31, second: 14, nanosecond: 789, millisecond: 123, microsecond: 456})"; + +pub const INVALID_DURATION_FORMAT_ERROR: &str = "A valid string representation of a duration or a map of duration components \n\ + Examples include: duration({days: 14, hours:16, minutes: 12}), duration({minutes: 1.5, seconds: 1, nanoseconds: 123456789}), duration('P14DT16H12M'), duration('PT0.75M'),"; + +pub const INVALID_DATE_FORMAT_ERROR: &str = "A valid string representation of a date or a map of date components.\n\ + Examples include: date('2015-07-21'), date('2015202'), date({year: 1984, week: 10, dayOfWeek: 3})"; diff --git a/core/src/interface/mod.rs b/core/src/interface/mod.rs index 620d69f..d7ddfc8 100644 --- a/core/src/interface/mod.rs +++ b/core/src/interface/mod.rs @@ -42,6 +42,21 @@ pub enum IndexError { Other(Box), } +impl PartialEq for IndexError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (IndexError::IOError, IndexError::IOError) => true, + (IndexError::NotSupported, IndexError::NotSupported) => true, + (IndexError::CorruptedData, IndexError::CorruptedData) => true, + (IndexError::ConnectionFailed(a), IndexError::ConnectionFailed(b)) => { + a.to_string() == b.to_string() + } + (IndexError::UnknownStore(a), IndexError::UnknownStore(b)) => a == b, + (IndexError::Other(a), IndexError::Other(b)) => a.to_string() == b.to_string(), + _ => false, + } + } +} // impl From for IndexError { // fn from(e: E) -> Self { // IndexError::Other(Box::new(e))