use super::function_trait::{Function, FunctionContext, FunctionError, FunctionResult};
use crate::storage::Value;
#[derive(Debug)]
pub struct UpperFunction;
impl UpperFunction {
pub fn new() -> Self {
Self
}
}
impl Function for UpperFunction {
fn name(&self) -> &str {
"UPPER"
}
fn description(&self) -> &str {
"Converts string values to uppercase"
}
fn argument_count(&self) -> usize {
1 }
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let string_val = if let Some(s) = value.as_string() {
s.to_string()
} else if let Some(n) = value.as_number() {
n.to_string()
} else {
match value {
Value::Boolean(b) => b.to_string(),
_ => return Ok(Value::Null), }
};
Ok(Value::String(string_val.to_uppercase()))
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}
#[derive(Debug)]
pub struct LowerFunction;
impl LowerFunction {
pub fn new() -> Self {
Self
}
}
impl Function for LowerFunction {
fn name(&self) -> &str {
"LOWER"
}
fn description(&self) -> &str {
"Converts string values to lowercase"
}
fn argument_count(&self) -> usize {
1 }
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let string_val = if let Some(s) = value.as_string() {
s.to_string()
} else if let Some(n) = value.as_number() {
n.to_string()
} else {
match value {
Value::Boolean(b) => b.to_string(),
_ => return Ok(Value::Null), }
};
Ok(Value::String(string_val.to_lowercase()))
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrimMode {
Both,
Leading,
Trailing,
}
#[derive(Debug)]
pub struct TrimFunction;
impl TrimFunction {
pub fn new() -> Self {
Self
}
fn parse_trim_mode(s: &str) -> Option<TrimMode> {
match s.to_uppercase().as_str() {
"LEADING" => Some(TrimMode::Leading),
"TRAILING" => Some(TrimMode::Trailing),
"BOTH" => Some(TrimMode::Both),
_ => None,
}
}
fn trim_string(input: &str, trim_chars: &str, mode: TrimMode) -> String {
match mode {
TrimMode::Leading => input
.trim_start_matches(|c: char| trim_chars.contains(c))
.to_string(),
TrimMode::Trailing => input
.trim_end_matches(|c: char| trim_chars.contains(c))
.to_string(),
TrimMode::Both => input
.trim_matches(|c: char| trim_chars.contains(c))
.to_string(),
}
}
}
impl Function for TrimFunction {
fn name(&self) -> &str {
"TRIM"
}
fn description(&self) -> &str {
"Removes leading and/or trailing characters from a string"
}
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 > 3 {
return Err(FunctionError::InvalidArgumentType {
message: "TRIM function expects 1 to 3 arguments".to_string(),
});
}
if arg_count == 1 {
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let string_val = self.value_to_string(value)?;
Ok(Value::String(string_val.trim().to_string()))
} else if arg_count == 2 {
let first_arg = context.get_argument(0)?;
let second_arg = context.get_argument(1)?;
if let Some(mode_str) = first_arg.as_string() {
if let Some(mode) = Self::parse_trim_mode(&mode_str) {
if second_arg.is_null() {
return Ok(Value::Null);
}
let string_val = self.value_to_string(second_arg)?;
let result = Self::trim_string(&string_val, " \t\n\r", mode);
return Ok(Value::String(result));
}
}
if first_arg.is_null() {
return Ok(Value::Null);
}
let string_val = self.value_to_string(first_arg)?;
let trim_char = self.extract_trim_char(second_arg)?;
Ok(Value::String(
string_val.trim_matches(trim_char).to_string(),
))
} else {
let mode_value = context.get_argument(0)?;
let char_value = context.get_argument(1)?;
let string_value = context.get_argument(2)?;
if string_value.is_null() {
return Ok(Value::Null);
}
let string_val = self.value_to_string(string_value)?;
let trim_chars = self.value_to_string(char_value)?;
let mode_str = if let Some(s) = mode_value.as_string() {
s
} else {
"BOTH"
};
let mode = Self::parse_trim_mode(&mode_str).unwrap_or(TrimMode::Both);
let result = Self::trim_string(&string_val, &trim_chars, mode);
Ok(Value::String(result))
}
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}
impl TrimFunction {
fn value_to_string(&self, value: &Value) -> FunctionResult<String> {
let string_val = if let Some(s) = value.as_string() {
s.to_string()
} else if let Some(n) = value.as_number() {
n.to_string()
} else {
match value {
Value::Boolean(b) => b.to_string(),
_ => return Ok(String::new()), }
};
Ok(string_val)
}
fn extract_trim_char(&self, value: &Value) -> FunctionResult<char> {
let trim_char = if let Some(s) = value.as_string() {
if s.is_empty() {
' ' } else {
s.chars().next().unwrap_or(' ')
}
} else {
' ' };
Ok(trim_char)
}
}
#[derive(Debug)]
pub struct SubstringFunction;
impl SubstringFunction {
pub fn new() -> Self {
Self
}
}
impl Function for SubstringFunction {
fn name(&self) -> &str {
"SUBSTRING"
}
fn description(&self) -> &str {
"Extracts a substring from a string starting at a position with optional length"
}
fn argument_count(&self) -> usize {
2
}
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
let arg_count = context.arguments.len();
if arg_count < 2 || arg_count > 3 {
return Err(FunctionError::InvalidArgumentType {
message: "SUBSTRING function expects 2 or 3 arguments".to_string(),
});
}
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let string_val = if let Some(s) = value.as_string() {
s.to_string()
} else if let Some(n) = value.as_number() {
n.to_string()
} else {
match value {
Value::Boolean(b) => b.to_string(),
_ => return Ok(Value::Null),
}
};
let start_value = context.get_argument(1)?;
let start_pos = if let Some(n) = start_value.as_number() {
let pos = n as i32;
if pos <= 0 {
0
} else {
(pos - 1) as usize }
} else {
return Err(FunctionError::InvalidArgumentType {
message: "SUBSTRING start position must be a number".to_string(),
});
};
let chars: Vec<char> = string_val.chars().collect();
if start_pos >= chars.len() {
return Ok(Value::String("".to_string()));
}
let result_string = if arg_count == 3 {
let length_value = context.get_argument(2)?;
let length = if let Some(n) = length_value.as_number() {
let len = n as i32;
if len <= 0 {
return Ok(Value::String("".to_string()));
}
len as usize
} else {
return Err(FunctionError::InvalidArgumentType {
message: "SUBSTRING length must be a number".to_string(),
});
};
let end_pos = std::cmp::min(start_pos + length, chars.len());
chars[start_pos..end_pos].iter().collect()
} else {
chars[start_pos..].iter().collect()
};
Ok(Value::String(result_string))
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}
#[derive(Debug)]
pub struct ReplaceFunction;
impl ReplaceFunction {
pub fn new() -> Self {
Self
}
}
impl Function for ReplaceFunction {
fn name(&self) -> &str {
"REPLACE"
}
fn description(&self) -> &str {
"Replaces all occurrences of a substring with another substring"
}
fn argument_count(&self) -> usize {
3 }
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
if context.arguments.len() != 3 {
return Err(FunctionError::InvalidArgumentCount {
expected: 3,
actual: context.arguments.len(),
});
}
let string_value = context.get_argument(0)?;
let search_value = context.get_argument(1)?;
let replacement_value = context.get_argument(2)?;
if string_value.is_null() || search_value.is_null() || replacement_value.is_null() {
return Ok(Value::Null);
}
let to_string = |value: &Value| -> Option<String> {
if let Some(s) = value.as_string() {
Some(s.to_string())
} else if let Some(n) = value.as_number() {
Some(n.to_string())
} else {
match value {
Value::Boolean(b) => Some(b.to_string()),
_ => None,
}
}
};
let string_val =
to_string(string_value).ok_or_else(|| FunctionError::InvalidArgumentType {
message: "First argument must be convertible to string".to_string(),
})?;
let search_val =
to_string(search_value).ok_or_else(|| FunctionError::InvalidArgumentType {
message: "Search argument must be convertible to string".to_string(),
})?;
let replacement_val =
to_string(replacement_value).ok_or_else(|| FunctionError::InvalidArgumentType {
message: "Replacement argument must be convertible to string".to_string(),
})?;
if search_val.is_empty() {
return Ok(Value::String(string_val));
}
let result = string_val.replace(&search_val, &replacement_val);
Ok(Value::String(result))
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}
#[derive(Debug)]
pub struct ReverseFunction;
impl ReverseFunction {
pub fn new() -> Self {
Self
}
}
impl Function for ReverseFunction {
fn name(&self) -> &str {
"REVERSE"
}
fn description(&self) -> &str {
"Reverses the characters in a string"
}
fn argument_count(&self) -> usize {
1 }
fn execute(&self, context: &FunctionContext) -> FunctionResult<Value> {
context.validate_argument_count(1)?;
let value = context.get_argument(0)?;
if value.is_null() {
return Ok(Value::Null);
}
let string_val = if let Some(s) = value.as_string() {
s.to_string()
} else if let Some(n) = value.as_number() {
n.to_string()
} else {
match value {
Value::Boolean(b) => b.to_string(),
_ => return Ok(Value::Null),
}
};
let reversed: String = string_val.chars().rev().collect();
Ok(Value::String(reversed))
}
fn return_type(&self) -> &str {
"String"
}
fn graph_context_required(&self) -> bool {
false }
}