use super::function_trait::{Function, FunctionContext, FunctionError, FunctionResult};
use crate::storage::Value;
#[derive(Debug)]
pub struct RoundFunction;
impl RoundFunction {
pub fn new() -> Self {
Self
}
}
impl Function for RoundFunction {
fn name(&self) -> &str {
"ROUND"
}
fn description(&self) -> &str {
"Rounds a number to specified decimal places"
}
fn argument_count(&self) -> usize {
1 }
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
let arg_count = context.arguments.len();
if arg_count == 0 || arg_count > 2 {
return Err(FunctionError::InvalidArgumentType {
message: "ROUND function expects 1 or 2 arguments".to_string(),
});
}
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let number = if let Some(n) = value.as_number() {
n
} else if let Some(s) = value.as_string() {
s.parse::<f64>()
.map_err(|_| FunctionError::InvalidArgumentType {
message: format!("Cannot convert '{}' to number", s),
})?
} else {
return Err(FunctionError::InvalidArgumentType {
message: "ROUND argument must be a number or convertible to number".to_string(),
});
};
let decimal_places = if arg_count == 2 {
let places_value = context.get_argument(1)?;
if let Some(n) = places_value.as_number() {
n as i32
} else {
return Err(FunctionError::InvalidArgumentType {
message: "ROUND decimal places argument must be a number".to_string(),
});
}
} else {
0
};
if number == 0.0 {
return Ok(Value::Number(0.0));
}
let rounded = oracle_round(number, decimal_places);
Ok(Value::Number(rounded))
}
fn return_type(&self) -> &str {
"Number"
}
}
fn oracle_round(n: f64, decimal_places: i32) -> f64 {
if n == 0.0 {
return 0.0;
}
if n < 0.0 {
return -oracle_round(-n, decimal_places);
}
let multiplier = 10.0_f64.powi(decimal_places);
let scaled = n * multiplier + 0.5;
let floored = scaled.floor();
floored / multiplier
}