use openscript::{Value, Error, Result};
use std::collections::HashMap;
pub fn split(string: &str, delimiter: &str) -> Result<Value> {
let parts: Vec<Value> = string
.split(delimiter)
.map(|s| Value::String(s.to_string()))
.collect();
Ok(Value::Array(parts))
}
pub fn join(array: &[Value], delimiter: &str) -> Result<Value> {
let joined = array
.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(delimiter);
Ok(Value::String(joined))
}
pub fn trim(string: &str) -> Result<Value> {
Ok(Value::String(string.trim().to_string()))
}
pub fn upper(string: &str) -> Result<Value> {
Ok(Value::String(string.to_uppercase()))
}
pub fn lower(string: &str) -> Result<Value> {
Ok(Value::String(string.to_lowercase()))
}
pub fn replace(string: &str, from: &str, to: &str) -> Result<Value> {
Ok(Value::String(string.replace(from, to)))
}
pub fn push(mut array: Vec<Value>, value: Value) -> Result<Value> {
array.push(value);
Ok(Value::Array(array))
}
pub fn pop(mut array: Vec<Value>) -> Result<Value> {
match array.pop() {
Some(value) => Ok(value),
None => Ok(Value::Null),
}
}
pub fn slice(array: &[Value], start: usize, end: usize) -> Result<Value> {
let slice: Vec<Value> = array
.iter()
.skip(start)
.take(end - start)
.cloned()
.collect();
Ok(Value::Array(slice))
}
pub fn sort(mut array: Vec<Value>) -> Result<Value> {
array.sort_by(|a, b| a.to_string().cmp(&b.to_string()));
Ok(Value::Array(array))
}
pub fn reverse(mut array: Vec<Value>) -> Result<Value> {
array.reverse();
Ok(Value::Array(array))
}
pub fn abs(num: f64) -> Result<Value> {
Ok(Value::Number(num.abs()))
}
pub fn floor(num: f64) -> Result<Value> {
Ok(Value::Number(num.floor()))
}
pub fn ceil(num: f64) -> Result<Value> {
Ok(Value::Number(num.ceil()))
}
pub fn round(num: f64) -> Result<Value> {
Ok(Value::Number(num.round()))
}
pub fn min(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Error::runtime_error("min() requires at least one argument"));
}
let mut min = args[0].to_number()?;
for arg in &args[1..] {
let num = arg.to_number()?;
if num < min {
min = num;
}
}
Ok(Value::Number(min))
}
pub fn max(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Error::runtime_error("max() requires at least one argument"));
}
let mut max = args[0].to_number()?;
for arg in &args[1..] {
let num = arg.to_number()?;
if num > max {
max = num;
}
}
Ok(Value::Number(max))
}
pub fn json_parse(json_str: &str) -> Result<Value> {
let parsed: serde_json::Value = serde_json::from_str(json_str)
.map_err(|e| Error::runtime_error(format!("Failed to parse JSON: {}", e)))?;
json_to_value(parsed)
}
pub fn json_stringify(value: &Value) -> Result<Value> {
let json_value = value_to_json(value)?;
let json_str = serde_json::to_string(&json_value)
.map_err(|e| Error::runtime_error(format!("Failed to stringify JSON: {}", e)))?;
Ok(Value::String(json_str))
}
pub fn json_to_value(json: serde_json::Value) -> Result<Value> {
match json {
serde_json::Value::Null => Ok(Value::Null),
serde_json::Value::Bool(b) => Ok(Value::Boolean(b)),
serde_json::Value::Number(n) => {
if let Some(f) = n.as_f64() {
Ok(Value::Number(f))
} else {
Err(Error::runtime_error("Invalid JSON number"))
}
}
serde_json::Value::String(s) => Ok(Value::String(s)),
serde_json::Value::Array(arr) => {
let values: Result<Vec<Value>> = arr.into_iter().map(json_to_value).collect();
Ok(Value::Array(values?))
}
serde_json::Value::Object(obj) => {
let mut map = HashMap::new();
for (key, value) in obj {
map.insert(key, json_to_value(value)?);
}
Ok(Value::Object(map))
}
}
}
pub fn value_to_json(value: &Value) -> Result<serde_json::Value> {
match value {
Value::Null => Ok(serde_json::Value::Null),
Value::Boolean(b) => Ok(serde_json::Value::Bool(*b)),
Value::Number(n) => Ok(serde_json::json!(*n)),
Value::String(s) => Ok(serde_json::Value::String(s.clone())),
Value::Array(arr) => {
let values: Result<Vec<serde_json::Value>> = arr.iter().map(value_to_json).collect();
Ok(serde_json::Value::Array(values?))
}
Value::Object(obj) => {
let mut map = serde_json::Map::new();
for (key, val) in obj {
map.insert(key.clone(), value_to_json(val)?);
}
Ok(serde_json::Value::Object(map))
}
_ => Err(Error::runtime_error("Cannot convert function to JSON")),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_utilities() {
let result = split("hello,world,rust", ",").unwrap();
match result {
Value::Array(arr) => {
assert_eq!(arr.len(), 3);
assert_eq!(arr[0], Value::String("hello".to_string()));
assert_eq!(arr[1], Value::String("world".to_string()));
assert_eq!(arr[2], Value::String("rust".to_string()));
}
_ => panic!("Expected array result from split"),
}
let array = vec![
Value::String("hello".to_string()),
Value::String("world".to_string()),
];
let result = join(&array, " ").unwrap();
assert_eq!(result, Value::String("hello world".to_string()));
let result = trim(" hello world ").unwrap();
assert_eq!(result, Value::String("hello world".to_string()));
let result = upper("hello").unwrap();
assert_eq!(result, Value::String("HELLO".to_string()));
let result = lower("HELLO").unwrap();
assert_eq!(result, Value::String("hello".to_string()));
let result = replace("hello world", "world", "rust").unwrap();
assert_eq!(result, Value::String("hello rust".to_string()));
}
#[test]
fn test_array_utilities() {
let array = vec![Value::Number(1.0), Value::Number(2.0)];
let result = push(array, Value::Number(3.0)).unwrap();
match result {
Value::Array(arr) => {
assert_eq!(arr.len(), 3);
assert_eq!(arr[2], Value::Number(3.0));
}
_ => panic!("Expected array result from push"),
}
let array = vec![Value::Number(1.0), Value::Number(2.0)];
let result = pop(array).unwrap();
assert_eq!(result, Value::Number(2.0));
let array = vec![
Value::Number(1.0),
Value::Number(2.0),
Value::Number(3.0),
Value::Number(4.0),
];
let result = slice(&array, 1, 3).unwrap();
match result {
Value::Array(arr) => {
assert_eq!(arr.len(), 2);
assert_eq!(arr[0], Value::Number(2.0));
assert_eq!(arr[1], Value::Number(3.0));
}
_ => panic!("Expected array result from slice"),
}
let array = vec![
Value::String("c".to_string()),
Value::String("a".to_string()),
Value::String("b".to_string()),
];
let result = sort(array).unwrap();
match result {
Value::Array(arr) => {
assert_eq!(arr[0], Value::String("a".to_string()));
assert_eq!(arr[1], Value::String("b".to_string()));
assert_eq!(arr[2], Value::String("c".to_string()));
}
_ => panic!("Expected array result from sort"),
}
let array = vec![
Value::Number(1.0),
Value::Number(2.0),
Value::Number(3.0),
];
let result = reverse(array).unwrap();
match result {
Value::Array(arr) => {
assert_eq!(arr[0], Value::Number(3.0));
assert_eq!(arr[1], Value::Number(2.0));
assert_eq!(arr[2], Value::Number(1.0));
}
_ => panic!("Expected array result from reverse"),
}
}
#[test]
fn test_math_utilities() {
let result = abs(-5.0).unwrap();
assert_eq!(result, Value::Number(5.0));
let result = floor(3.7).unwrap();
assert_eq!(result, Value::Number(3.0));
let result = ceil(3.2).unwrap();
assert_eq!(result, Value::Number(4.0));
let result = round(3.5).unwrap();
assert_eq!(result, Value::Number(4.0));
let args = vec![
Value::Number(5.0),
Value::Number(2.0),
Value::Number(8.0),
Value::Number(1.0),
];
let result = min(&args).unwrap();
assert_eq!(result, Value::Number(1.0));
let args = vec![
Value::Number(5.0),
Value::Number(2.0),
Value::Number(8.0),
Value::Number(1.0),
];
let result = max(&args).unwrap();
assert_eq!(result, Value::Number(8.0));
}
#[test]
fn test_json_utilities() {
let result = json_parse(r#"{"name":"test","value":42}"#).unwrap();
match result {
Value::Object(obj) => {
assert_eq!(obj.get("name").unwrap(), &Value::String("test".to_string()));
assert_eq!(obj.get("value").unwrap(), &Value::Number(42.0));
}
_ => panic!("Expected object result from json_parse"),
}
let mut obj = HashMap::new();
obj.insert("name".to_string(), Value::String("test".to_string()));
obj.insert("value".to_string(), Value::Number(42.0));
let result = json_stringify(&Value::Object(obj)).unwrap();
match result {
Value::String(s) => {
assert!(s.contains("\"name\":\"test\""));
assert!(s.contains("\"value\":42"));
}
_ => panic!("Expected string result from json_stringify"),
}
}
}