🦀 🔧 Custom @ Operators in Rust

Rust Documentation

🔧 Custom @ Operators in Rust

TuskLang allows you to create custom @ operators in Rust, extending the language with domain-specific functionality while maintaining type safety and performance.

Basic Custom Operator Structure

// Define a custom operator trait
trait CustomOperator {
    fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>>;
    fn name(&self) -> &'static str;
    fn description(&self) -> &'static str;
}

// Implement a custom operator struct UserCountOperator;

impl CustomOperator for UserCountOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.is_empty() { return Err("@user_count requires at least one argument".into()); } let status = args[0].as_str().unwrap_or("active"); let count = @query("SELECT COUNT(*) FROM users WHERE status = ?", vec![status])?; Ok(Value::Number(count as u64)) } fn name(&self) -> &'static str { "user_count" } fn description(&self) -> &'static str { "Count users by status" } }

Registering Custom Operators

// Register custom operators
fn register_custom_operators(registry: &mut OperatorRegistry) {
    registry.register(Box::new(UserCountOperator));
    registry.register(Box::new(OrderTotalOperator));
    registry.register(Box::new(DataValidationOperator));
    registry.register(Box::new(CacheOperator));
    registry.register(Box::new(NotificationOperator));
}

// Usage in TuskLang let active_users = @user_count("active"); let pending_users = @user_count("pending");

Database Custom Operators

// Custom database operator
struct OrderTotalOperator;

impl CustomOperator for OrderTotalOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 1 { return Err("@order_total requires user_id".into()); } let user_id = args[0].as_u64().unwrap_or(0); let total = @query( "SELECT SUM(total) FROM orders WHERE user_id = ? AND status = 'completed'", vec![user_id] )?; Ok(Value::Number(total as u64)) } fn name(&self) -> &'static str { "order_total" } fn description(&self) -> &'static str { "Calculate total order value for a user" } }

// Usage let user_total = @order_total(@user.id); let formatted_total = format!("${:.2}", user_total as f64 / 100.0);

Validation Custom Operators

// Custom validation operator
struct DataValidationOperator;

impl CustomOperator for DataValidationOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@validate requires data and rules".into()); } let data = &args[0]; let rules = &args[1]; let mut errors = Vec::new(); // Validate email if let Some(email) = data.get("email").and_then(|e| e.as_str()) { if !@is_valid_email(email) { errors.push("Invalid email format".to_string()); } } // Validate age if let Some(age) = data.get("age").and_then(|a| a.as_u64()) { if age < 18 { errors.push("User must be 18 or older".to_string()); } } if errors.is_empty() { Ok(Value::Bool(true)) } else { Ok(Value::Object(errors.into_iter().map(|e| (e, Value::Null)).collect())) } } fn name(&self) -> &'static str { "validate" } fn description(&self) -> &'static str { "Validate data against rules" } }

// Usage let validation_result = @validate(@user_data, @validation_rules); if validation_result.is_object() { @log("Validation errors: {:?}", validation_result); }

Cache Custom Operators

// Custom cache operator
struct CacheOperator;

impl CustomOperator for CacheOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@cache requires key and value".into()); } let key = args[0].as_str().unwrap_or(""); let value = &args[1]; let ttl = args.get(2).and_then(|t| t.as_u64()).unwrap_or(3600); @cache.put(key, value, Duration::from_secs(ttl))?; Ok(Value::Bool(true)) } fn name(&self) -> &'static str { "cache" } fn description(&self) -> &'static str { "Cache a value with optional TTL" } }

// Usage @cache("user_profile", @user_profile, 7200); let cached_profile = @cache.get("user_profile");

Notification Custom Operators

// Custom notification operator
struct NotificationOperator;

impl CustomOperator for NotificationOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 3 { return Err("@notify requires user_id, message, and type".into()); } let user_id = args[0].as_u64().unwrap_or(0); let message = args[1].as_str().unwrap_or(""); let notification_type = args[2].as_str().unwrap_or("info"); let notification = Notification { user_id, message: message.to_string(), notification_type: notification_type.to_string(), created_at: chrono::Utc::now(), }; @save_notification(notification)?; @send_push_notification(user_id, message)?; Ok(Value::Bool(true)) } fn name(&self) -> &'static str { "notify" } fn description(&self) -> &'static str { "Send notification to user" } }

// Usage @notify(@user.id, "Your order has been shipped!", "success");

File System Custom Operators

// Custom file operator
struct FileOperator;

impl CustomOperator for FileOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@file requires action and path".into()); } let action = args[0].as_str().unwrap_or(""); let path = args[1].as_str().unwrap_or(""); match action { "read" => { let content = std::fs::read_to_string(path)?; Ok(Value::String(content)) } "write" => { if args.len() < 3 { return Err("@file write requires content".into()); } let content = args[2].as_str().unwrap_or(""); std::fs::write(path, content)?; Ok(Value::Bool(true)) } "exists" => { let exists = std::path::Path::new(path).exists(); Ok(Value::Bool(exists)) } _ => Err(format!("Unknown file action: {}", action).into()) } } fn name(&self) -> &'static str { "file" } fn description(&self) -> &'static str { "File system operations" } }

// Usage let config_content = @file("read", "config.tusk"); @file("write", "log.txt", @log_content); let file_exists = @file("exists", "data.json");

HTTP Custom Operators

// Custom HTTP operator
struct HttpOperator;

impl CustomOperator for HttpOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@http requires method and url".into()); } let method = args[0].as_str().unwrap_or("GET"); let url = args[1].as_str().unwrap_or(""); let body = args.get(2); let headers = args.get(3); let client = reqwest::Client::new(); let mut request = match method { "GET" => client.get(url), "POST" => client.post(url), "PUT" => client.put(url), "DELETE" => client.delete(url), _ => return Err(format!("Unsupported HTTP method: {}", method).into()) }; if let Some(body_data) = body { request = request.json(body_data); } if let Some(header_data) = headers { if let Some(header_map) = header_data.as_object() { for (key, value) in header_map { if let Some(value_str) = value.as_str() { request = request.header(key, value_str); } } } } let response = request.send().await?; let status = response.status().as_u16(); let body = response.text().await?; Ok(serde_json::json!({ "status": status, "body": body })) } fn name(&self) -> &'static str { "http" } fn description(&self) -> &'static str { "HTTP requests" } }

// Usage let api_response = @http("GET", "https://api.example.com/users"); let post_result = @http("POST", "https://api.example.com/users", @user_data, @headers);

Encryption Custom Operators

// Custom encryption operator
struct EncryptionOperator;

impl CustomOperator for EncryptionOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@encrypt requires action and data".into()); } let action = args[0].as_str().unwrap_or(""); let data = args[1].as_str().unwrap_or(""); let key = args.get(2).and_then(|k| k.as_str()).unwrap_or("default_key"); match action { "encrypt" => { let encrypted = @encrypt_data(data, key)?; Ok(Value::String(encrypted)) } "decrypt" => { let decrypted = @decrypt_data(data, key)?; Ok(Value::String(decrypted)) } _ => Err(format!("Unknown encryption action: {}", action).into()) } } fn name(&self) -> &'static str { "encrypt" } fn description(&self) -> &'static str { "Encryption and decryption operations" } }

// Usage let encrypted_password = @encrypt("encrypt", @user.password, @secret_key); let decrypted_data = @encrypt("decrypt", @encrypted_data, @secret_key);

Date/Time Custom Operators

// Custom date operator
struct DateOperator;

impl CustomOperator for DateOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 1 { return Err("@date requires action".into()); } let action = args[0].as_str().unwrap_or(""); match action { "now" => { let now = chrono::Utc::now(); Ok(Value::String(now.to_rfc3339())) } "format" => { if args.len() < 3 { return Err("@date format requires format string and date".into()); } let format_str = args[1].as_str().unwrap_or("%Y-%m-%d"); let date_str = args[2].as_str().unwrap_or(""); let date = chrono::DateTime::parse_from_rfc3339(date_str)?; let formatted = date.format(format_str).to_string(); Ok(Value::String(formatted)) } "add_days" => { if args.len() < 3 { return Err("@date add_days requires date and days".into()); } let date_str = args[1].as_str().unwrap_or(""); let days = args[2].as_i64().unwrap_or(0); let date = chrono::DateTime::parse_from_rfc3339(date_str)?; let new_date = date + chrono::Duration::days(days); Ok(Value::String(new_date.to_rfc3339())) } _ => Err(format!("Unknown date action: {}", action).into()) } } fn name(&self) -> &'static str { "date" } fn description(&self) -> &'static str { "Date and time operations" } }

// Usage let current_time = @date("now"); let formatted_date = @date("format", "%Y-%m-%d", @user.created_at); let future_date = @date("add_days", @current_date, 7);

Math Custom Operators

// Custom math operator
struct MathOperator;

impl CustomOperator for MathOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@math requires operation and values".into()); } let operation = args[0].as_str().unwrap_or(""); let values: Vec<f64> = args[1..].iter() .filter_map(|v| v.as_f64()) .collect(); if values.is_empty() { return Err("No numeric values provided".into()); } let result = match operation { "sum" => values.iter().sum(), "avg" => values.iter().sum::<f64>() / values.len() as f64, "min" => values.iter().fold(f64::INFINITY, |a, &b| a.min(b)), "max" => values.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)), "round" => values[0].round(), "floor" => values[0].floor(), "ceil" => values[0].ceil(), _ => return Err(format!("Unknown math operation: {}", operation).into()) }; Ok(Value::Number(serde_json::Number::from_f64(result).unwrap())) } fn name(&self) -> &'static str { "math" } fn description(&self) -> &'static str { "Mathematical operations" } }

// Usage let total = @math("sum", @prices); let average = @math("avg", @scores); let rounded = @math("round", @price);

String Custom Operators

// Custom string operator
struct StringOperator;

impl CustomOperator for StringOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { if args.len() < 2 { return Err("@string requires operation and text".into()); } let operation = args[0].as_str().unwrap_or(""); let text = args[1].as_str().unwrap_or(""); let result = match operation { "upper" => text.to_uppercase(), "lower" => text.to_lowercase(), "title" => text.split_whitespace() .map(|word| { let mut chars = word.chars(); match chars.next() { None => String::new(), Some(first) => first.to_uppercase().chain(chars).collect(), } }) .collect::<Vec<_>>() .join(" "), "slug" => text .to_lowercase() .chars() .map(|c| if c.is_alphanumeric() { c } else { '-' }) .collect::<String>() .trim_matches('-') .to_string(), "reverse" => text.chars().rev().collect(), "length" => return Ok(Value::Number(text.len() as u64)), _ => return Err(format!("Unknown string operation: {}", operation).into()) }; Ok(Value::String(result)) } fn name(&self) -> &'static str { "string" } fn description(&self) -> &'static str { "String manipulation operations" } }

// Usage let upper_text = @string("upper", @user.name); let slug = @string("slug", @post.title); let text_length = @string("length", @content);

Best Practices

1. Error Handling

// Always provide meaningful error messages
impl CustomOperator for MyOperator {
    fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> {
        if args.is_empty() {
            return Err("@my_operator requires at least one argument".into());
        }
        
        // Validate argument types
        let user_id = args[0].as_u64()
            .ok_or("First argument must be a number")?;
        
        // Your logic here
        Ok(Value::Bool(true))
    }
}

2. Type Safety

// Use Rust's type system for safety
fn validate_args(args: &[Value], expected_types: &[&str]) -> Result<(), Box<dyn std::error::Error>> {
    if args.len() != expected_types.len() {
        return Err(format!("Expected {} arguments, got {}", expected_types.len(), args.len()).into());
    }
    
    for (arg, expected_type) in args.iter().zip(expected_types.iter()) {
        match *expected_type {
            "string" => { arg.as_str().ok_or("Expected string")?; }
            "number" => { arg.as_u64().ok_or("Expected number")?; }
            "bool" => { arg.as_bool().ok_or("Expected boolean")?; }
            _ => return Err(format!("Unknown type: {}", expected_type).into())
        }
    }
    
    Ok(())
}

3. Performance Considerations

// Use efficient data structures
impl CustomOperator for EfficientOperator {
    fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> {
        // Use references to avoid cloning
        let data = args[0].as_str().unwrap_or("");
        
        // Use efficient algorithms
        let result = data
            .chars()
            .filter(|c| c.is_alphanumeric())
            .collect::<String>();
        
        Ok(Value::String(result))
    }
}

4. Documentation

// Provide comprehensive documentation
impl CustomOperator for DocumentedOperator {
    fn name(&self) -> &'static str {
        "documented"
    }
    
    fn description(&self) -> &'static str {
        "A well-documented custom operator that demonstrates best practices"
    }
    
    fn help(&self) -> &'static str {
        "Usage: @documented(arg1, arg2)\n\
         arg1: string - The input text\n\
         arg2: number - The processing limit\n\
         Returns: string - The processed result"
    }
}

5. Testing

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_custom_operator() {
        let operator = MyCustomOperator;
        let args = vec![Value::String("test".to_string())];
        
        let result = operator.execute(&args);
        assert!(result.is_ok());
    }
}

Custom @ operators in Rust provide a powerful way to extend TuskLang with domain-specific functionality while maintaining Rust's safety and performance characteristics.