🦀 ⚠️ @ Operator Errors in Rust

Rust Documentation

⚠️ @ Operator Errors in Rust

TuskLang provides comprehensive error handling for @ operators in Rust, ensuring robust applications with proper error recovery and debugging capabilities.

Error Types

// Define custom error types for @ operators
#[derive(Debug, thiserror::Error)]
pub enum TuskOperatorError {
    #[error("Invalid argument count: expected {expected}, got {actual}")]
    InvalidArgumentCount { expected: usize, actual: usize },
    
    #[error("Invalid argument type: expected {expected}, got {actual}")]
    InvalidArgumentType { expected: String, actual: String },
    
    #[error("Database error: {message}")]
    DatabaseError { message: String },
    
    #[error("Network error: {message}")]
    NetworkError { message: String },
    
    #[error("Validation error: {field} - {message}")]
    ValidationError { field: String, message: String },
    
    #[error("Cache error: {message}")]
    CacheError { message: String },
    
    #[error("File system error: {message}")]
    FileSystemError { message: String },
    
    #[error("Custom operator error: {message}")]
    CustomOperatorError { message: String },
}

// Error context for better debugging #[derive(Debug)] pub struct ErrorContext { pub operator: String, pub arguments: Vec<String>, pub file: String, pub line: u32, pub column: u32, }

Basic Error Handling

// Handle @ operator errors with Result
fn process_user_data(user_id: u32) -> Result<UserData, TuskOperatorError> {
    let user = @query("SELECT * FROM users WHERE id = ?", vec![user_id])
        .map_err(|e| TuskOperatorError::DatabaseError {
            message: format!("Failed to fetch user: {}", e)
        })?;
    
    let posts = @query("SELECT * FROM posts WHERE user_id = ?", vec![user_id])
        .map_err(|e| TuskOperatorError::DatabaseError {
            message: format!("Failed to fetch posts: {}", e)
        })?;
    
    Ok(UserData { user, posts })
}

// Error handling with context fn safe_operator_call<F, T>(operator: &str, args: &[Value], f: F) -> Result<T, TuskOperatorError> where F: FnOnce(&[Value]) -> Result<T, Box<dyn std::error::Error>>, { f(args).map_err(|e| TuskOperatorError::CustomOperatorError { message: format!("@{} failed: {}", operator, e) }) }

Argument Validation Errors

// Validate @ operator arguments
fn validate_arguments(args: &[Value], expected_count: usize, expected_types: &[&str]) -> Result<(), TuskOperatorError> {
    // Check argument count
    if args.len() != expected_count {
        return Err(TuskOperatorError::InvalidArgumentCount {
            expected: expected_count,
            actual: args.len(),
        });
    }
    
    // Check argument types
    for (i, (arg, expected_type)) in args.iter().zip(expected_types.iter()).enumerate() {
        let actual_type = match arg {
            Value::String(_) => "string",
            Value::Number(_) => "number",
            Value::Bool(_) => "boolean",
            Value::Array(_) => "array",
            Value::Object(_) => "object",
            Value::Null => "null",
        };
        
        if actual_type != *expected_type {
            return Err(TuskOperatorError::InvalidArgumentType {
                expected: expected_type.to_string(),
                actual: actual_type.to_string(),
            });
        }
    }
    
    Ok(())
}

// Usage in custom operators impl CustomOperator for MyOperator { fn execute(&self, args: &[Value]) -> Result<Value, Box<dyn std::error::Error>> { validate_arguments(args, 2, &["string", "number"])?; let text = args[0].as_str().unwrap(); let count = args[1].as_u64().unwrap(); // Process arguments... Ok(Value::String(text.repeat(count as usize))) } }

Database Error Handling

// Handle database @ operator errors
fn safe_database_query(sql: &str, params: Vec<Value>) -> Result<Vec<Value>, TuskOperatorError> {
    @query(sql, params).map_err(|e| {
        TuskOperatorError::DatabaseError {
            message: format!("Query failed: {} - SQL: {}", e, sql)
        }
    })
}

// Transaction error handling fn execute_transaction<F, T>(f: F) -> Result<T, TuskOperatorError> where F: FnOnce(&mut Transaction) -> Result<T, Box<dyn std::error::Error>>, { @db.transaction(|tx| { f(tx).map_err(|e| TuskOperatorError::DatabaseError { message: format!("Transaction failed: {}", e) }) }).map_err(|e| TuskOperatorError::DatabaseError { message: format!("Transaction error: {}", e) }) }

// Usage let result = execute_transaction(|tx| { let user = tx.table("users").insert(@user_data)?; let profile = tx.table("profiles").insert(ProfileData { user_id: user.id, bio: @bio, })?; Ok((user, profile)) })?;

Network Error Handling

// Handle HTTP @ operator errors
async fn safe_http_request(method: &str, url: &str, body: Option<Value>) -> Result<Value, TuskOperatorError> {
    @http(method, url, body).await.map_err(|e| {
        TuskOperatorError::NetworkError {
            message: format!("HTTP request failed: {} {} - {}", method, url, e)
        }
    })
}

// Retry logic for network operations async fn retry_http_request(method: &str, url: &str, max_retries: u32) -> Result<Value, TuskOperatorError> { let mut last_error = None; for attempt in 1..=max_retries { match @http(method, url, None).await { Ok(response) => return Ok(response), Err(e) => { last_error = Some(e); if attempt < max_retries { tokio::time::sleep(tokio::time::Duration::from_secs(2u64.pow(attempt))).await; } } } } Err(TuskOperatorError::NetworkError { message: format!("HTTP request failed after {} attempts: {}", max_retries, last_error.unwrap_or_else(|| "Unknown error".into())) }) }

Validation Error Handling

// Handle validation @ operator errors
fn validate_user_data(data: &Value) -> Result<(), Vec<TuskOperatorError>> {
    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(TuskOperatorError::ValidationError {
                field: "email".to_string(),
                message: "Invalid email format".to_string(),
            });
        }
    } else {
        errors.push(TuskOperatorError::ValidationError {
            field: "email".to_string(),
            message: "Email is required".to_string(),
        });
    }
    
    // Validate age
    if let Some(age) = data.get("age").and_then(|a| a.as_u64()) {
        if age < 18 {
            errors.push(TuskOperatorError::ValidationError {
                field: "age".to_string(),
                message: "User must be 18 or older".to_string(),
            });
        }
    }
    
    if errors.is_empty() {
        Ok(())
    } else {
        Err(errors)
    }
}

// Usage let validation_result = validate_user_data(&@user_data); match validation_result { Ok(()) => { @save_user(@user_data)?; } Err(errors) => { for error in errors { @log("Validation error: {:?}", error); } return Err(TuskOperatorError::ValidationError { field: "user_data".to_string(), message: "User data validation failed".to_string(), }); } }

Cache Error Handling

// Handle cache @ operator errors
fn safe_cache_operation<F, T>(key: &str, f: F) -> Result<T, TuskOperatorError>
where
    F: FnOnce() -> Result<T, Box<dyn std::error::Error>>,
{
    @cache.get(key).map_err(|e| TuskOperatorError::CacheError {
        message: format!("Cache get failed for key '{}': {}", key, e)
    }).or_else(|_| {
        // Cache miss, execute function and store result
        let result = f().map_err(|e| TuskOperatorError::CacheError {
            message: format!("Cache fallback failed for key '{}': {}", key, e)
        })?;
        
        @cache.put(key, &result, Duration::from_secs(3600))
            .map_err(|e| TuskOperatorError::CacheError {
                message: format!("Cache put failed for key '{}': {}", key, e)
            })?;
        
        Ok(result)
    })
}

// Usage let user_data = safe_cache_operation(&format!("user:{}", @user_id), || { @query("SELECT * FROM users WHERE id = ?", vec![@user_id]) })?;

File System Error Handling

// Handle file @ operator errors
fn safe_file_operation(action: &str, path: &str, content: Option<&str>) -> Result<Value, TuskOperatorError> {
    match action {
        "read" => {
            std::fs::read_to_string(path)
                .map(Value::String)
                .map_err(|e| TuskOperatorError::FileSystemError {
                    message: format!("Failed to read file '{}': {}", path, e)
                })
        }
        "write" => {
            let content = content.ok_or_else(|| TuskOperatorError::FileSystemError {
                message: "Content required for write operation".to_string()
            })?;
            
            std::fs::write(path, content)
                .map(|_| Value::Bool(true))
                .map_err(|e| TuskOperatorError::FileSystemError {
                    message: format!("Failed to write file '{}': {}", path, e)
                })
        }
        "exists" => {
            Ok(Value::Bool(std::path::Path::new(path).exists()))
        }
        _ => Err(TuskOperatorError::FileSystemError {
            message: format!("Unknown file action: {}", action)
        })
    }
}

// Usage let config_content = safe_file_operation("read", "config.tusk", None)?; safe_file_operation("write", "log.txt", Some(&@log_content))?;

Error Recovery Strategies

// Implement error recovery strategies
enum RecoveryStrategy {
    Retry { max_attempts: u32, delay_ms: u64 },
    Fallback { value: Value },
    CircuitBreaker { threshold: u32, timeout_ms: u64 },
    GracefulDegradation { degraded_function: Box<dyn Fn() -> Result<Value, TuskOperatorError>> },
}

fn execute_with_recovery<F>(operation: F, strategy: RecoveryStrategy) -> Result<Value, TuskOperatorError> where F: Fn() -> Result<Value, TuskOperatorError>, { match strategy { RecoveryStrategy::Retry { max_attempts, delay_ms } => { let mut last_error = None; for attempt in 1..=max_attempts { match operation() { Ok(result) => return Ok(result), Err(e) => { last_error = Some(e); if attempt < max_attempts { std::thread::sleep(std::time::Duration::from_millis(delay_ms)); } } } } Err(last_error.unwrap_or_else(|| TuskOperatorError::CustomOperatorError { message: "Operation failed after all retry attempts".to_string() })) } RecoveryStrategy::Fallback { value } => { operation().or(Ok(value)) } RecoveryStrategy::CircuitBreaker { threshold, timeout_ms } => { // Implement circuit breaker pattern static mut FAILURE_COUNT: u32 = 0; static mut LAST_FAILURE_TIME: u64 = 0; unsafe { let now = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_millis() as u64; if FAILURE_COUNT >= threshold && (now - LAST_FAILURE_TIME) < timeout_ms { return Err(TuskOperatorError::CustomOperatorError { message: "Circuit breaker is open".to_string() }); } match operation() { Ok(result) => { FAILURE_COUNT = 0; Ok(result) } Err(e) => { FAILURE_COUNT += 1; LAST_FAILURE_TIME = now; Err(e) } } } } RecoveryStrategy::GracefulDegradation { degraded_function } => { operation().or_else(|_| degraded_function()) } } }

// Usage let result = execute_with_recovery( || @query("SELECT * FROM users"), RecoveryStrategy::Retry { max_attempts: 3, delay_ms: 1000 } )?;

let cached_result = execute_with_recovery( || @cache.get("expensive_operation"), RecoveryStrategy::Fallback { value: Value::String("default".to_string()) } )?;

Error Logging and Monitoring

// Comprehensive error logging
fn log_operator_error(error: &TuskOperatorError, context: &ErrorContext) {
    let error_message = match error {
        TuskOperatorError::DatabaseError { message } => {
            format!("Database error in @{}: {}", context.operator, message)
        }
        TuskOperatorError::NetworkError { message } => {
            format!("Network error in @{}: {}", context.operator, message)
        }
        TuskOperatorError::ValidationError { field, message } => {
            format!("Validation error in @{} for field '{}': {}", context.operator, field, message)
        }
        _ => format!("Error in @{}: {:?}", context.operator, error),
    };
    
    @log("ERROR: {} at {}:{}:{}", 
         error_message, 
         context.file, 
         context.line, 
         context.column);
    
    // Send to monitoring service
    @metrics.increment("tusk_operator_errors", 1);
    @metrics.increment(&format!("tusk_operator_errors.{}", context.operator), 1);
}

// Error context capture macro_rules! operator_call { ($operator:expr, $($args:expr),*) => {{ let context = ErrorContext { operator: stringify!($operator).to_string(), arguments: vec![$(format!("{:?}", $args)),*], file: file!().to_string(), line: line!(), column: column!(), }; match $operator($($args),*) { Ok(result) => Ok(result), Err(e) => { log_operator_error(&e, &context); Err(e) } } }}; }

// Usage let user_data = operator_call!(@query, "SELECT * FROM users WHERE id = ?", @user_id)?;

Best Practices

1. Use Specific Error Types

// Define specific error types for different operations
#[derive(Debug, thiserror::Error)]
pub enum UserOperationError {
    #[error("User not found: {id}")]
    UserNotFound { id: u32 },
    
    #[error("User already exists: {email}")]
    UserAlreadyExists { email: String },
    
    #[error("Invalid user data: {message}")]
    InvalidUserData { message: String },
}

// Use specific error types in operations fn create_user(user_data: UserData) -> Result<User, UserOperationError> { if @user_exists(user_data.email) { return Err(UserOperationError::UserAlreadyExists { email: user_data.email, }); } @save_user(user_data).map_err(|e| UserOperationError::InvalidUserData { message: e.to_string(), }) }

2. Provide Context in Errors

// Include context in error messages
fn process_order(order_id: u32) -> Result<Order, TuskOperatorError> {
    @query("SELECT * FROM orders WHERE id = ?", vec![order_id])
        .map_err(|e| TuskOperatorError::DatabaseError {
            message: format!("Failed to fetch order {}: {}", order_id, e)
        })?
        .ok_or_else(|| TuskOperatorError::CustomOperatorError {
            message: format!("Order {} not found", order_id)
        })
}

3. Implement Proper Error Recovery

// Implement appropriate recovery strategies
fn get_user_data(user_id: u32) -> Result<UserData, TuskOperatorError> {
    // Try cache first
    if let Ok(cached) = @cache.get(&format!("user:{}", user_id)) {
        return Ok(cached);
    }
    
    // Fallback to database
    let user_data = @query("SELECT * FROM users WHERE id = ?", vec![user_id])
        .map_err(|e| TuskOperatorError::DatabaseError {
            message: format!("Failed to fetch user {}: {}", user_id, e)
        })?;
    
    // Cache for next time
    @cache.put(&format!("user:{}", user_id), &user_data, Duration::from_secs(3600))
        .map_err(|e| TuskOperatorError::CacheError {
            message: format!("Failed to cache user data: {}", e)
        })?;
    
    Ok(user_data)
}

4. Use Error Propagation

// Use the ? operator for clean error propagation
fn process_user_request(request: UserRequest) -> Result<UserResponse, TuskOperatorError> {
    let user = @validate_user(request.user_id)?;
    let permissions = @get_user_permissions(user.id)?;
    let data = @process_user_data(user, permissions)?;
    
    Ok(UserResponse { user, data })
}

5. Monitor and Alert on Errors

// Set up error monitoring
fn monitor_operator_errors() {
    @metrics.gauge("tusk_operator_error_rate", @calculate_error_rate());
    
    if @error_rate_exceeds_threshold() {
        @alert.send("High TuskLang operator error rate detected");
    }
}

The @ operator error handling in Rust provides robust error management with proper recovery strategies, comprehensive logging, and type-safe error propagation.