openscript_sdk 0.1.0

Standard library and AI integration for OpenScript
Documentation
//! Utility functions for OpenScript

use openscript::{Value, Error, Result};
use std::collections::HashMap;

// String utilities
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)))
}

// Array utilities
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))
}

// Math utilities
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))
}

// JSON utilities
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() {
        // Test split
        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"),
        }

        // Test join
        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()));

        // Test trim
        let result = trim("  hello world  ").unwrap();
        assert_eq!(result, Value::String("hello world".to_string()));

        // Test upper
        let result = upper("hello").unwrap();
        assert_eq!(result, Value::String("HELLO".to_string()));

        // Test lower
        let result = lower("HELLO").unwrap();
        assert_eq!(result, Value::String("hello".to_string()));

        // Test replace
        let result = replace("hello world", "world", "rust").unwrap();
        assert_eq!(result, Value::String("hello rust".to_string()));
    }

    #[test]
    fn test_array_utilities() {
        // Test push
        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"),
        }

        // Test pop
        let array = vec![Value::Number(1.0), Value::Number(2.0)];
        let result = pop(array).unwrap();
        assert_eq!(result, Value::Number(2.0));

        // Test slice
        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"),
        }

        // Test sort
        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"),
        }

        // Test reverse
        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() {
        // Test abs
        let result = abs(-5.0).unwrap();
        assert_eq!(result, Value::Number(5.0));

        // Test floor
        let result = floor(3.7).unwrap();
        assert_eq!(result, Value::Number(3.0));

        // Test ceil
        let result = ceil(3.2).unwrap();
        assert_eq!(result, Value::Number(4.0));

        // Test round
        let result = round(3.5).unwrap();
        assert_eq!(result, Value::Number(4.0));

        // Test min
        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));

        // Test max
        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() {
        // Test json_parse
        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"),
        }

        // Test json_stringify
        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"),
        }
    }
}