use rust_decimal::Decimal;
use crate::error::QueryError;
use super::super::Executor;
use super::super::types::Value;
impl Executor<'_> {
pub(crate) fn round_on_values(args: &[Value]) -> Result<Value, QueryError> {
if args.is_empty() || args.len() > 2 {
return Err(QueryError::InvalidArguments(
"ROUND".to_string(),
"expected 1 or 2 arguments".to_string(),
));
}
let val = args[0].clone();
let decimals: i64 = if args.len() == 2 {
match &args[1] {
Value::Integer(i) => *i,
_ => {
return Err(QueryError::Type(
"ROUND second arg must be integer".to_string(),
));
}
}
} else {
0
};
match val {
Value::Number(n) => Ok(Value::Number(round_decimal(n, decimals))),
Value::Integer(i) => {
if decimals >= 0 {
Ok(Value::Integer(i))
} else {
use rust_decimal::prelude::ToPrimitive;
let r = round_decimal(Decimal::from(i), decimals);
Ok(r.to_i64().map_or(Value::Number(r), Value::Integer))
}
}
_ => Err(QueryError::Type("ROUND expects a number".to_string())),
}
}
}
fn round_decimal(n: Decimal, places: i64) -> Decimal {
if places >= 0 {
return n.round_dp(u32::try_from(places).unwrap_or(u32::MAX).min(28));
}
let Some(k) = places.checked_neg() else {
return Decimal::ZERO;
};
if k > 28 {
return Decimal::ZERO;
}
let scale = Decimal::from_i128_with_scale(10i128.pow(k as u32), 0);
(n / scale).round() * scale
}