sapphire-lang 0.7.0

Gradually typed scripting language where every value is an object and types are optional, checked at runtime
Documentation
use crate::vm::{VmError, VmValue};
use VmValue::Str;

const METHOD_ARITIES: &[(&str, usize)] = &[
    ("exist?", 1),
    ("read", 1),
    ("write", 2),
];

fn arg_error(name: &str, argc: usize, line: u32) -> VmError {
    let msg = METHOD_ARITIES.iter()
        .find(|(n, _)| *n == name)
        .map(|(_, arity)| format!("File.{name} expects {arity} String argument(s), got {argc}"))
        .unwrap_or_else(|| format!("File has no class method '{name}'"));
    VmError::TypeError { message: msg, line }
}

pub fn dispatch_file_class_method(name: &str, args: &[VmValue], line: u32) -> Result<VmValue, VmError> {
    match (name, args) {
        ("exist?", [Str(path)]) =>
            Ok(VmValue::Bool(std::path::Path::new(path.as_str()).exists())),

        ("read", [Str(path)]) =>
            std::fs::read_to_string(path.as_str())
                .map(Str)
                .map_err(|e| VmError::Raised(Str(format!("{path}: {e}")))),

        ("write", [Str(path), Str(content)]) =>
            std::fs::write(path.as_str(), content.as_str())
                .map(|_| VmValue::Nil)
                .map_err(|e| VmError::Raised(Str(format!("{path}: {e}")))),

        _ => Err(arg_error(name, args.len(), line)),
    }
}