Skip to main content

aver/services/
console.rs

1/// Console service — terminal I/O.
2///
3/// Methods:
4///   Console.print(msg)    — print to stdout (same as top-level `print`)
5///   Console.error(msg)    — print to stderr (no colour, raw message)
6///   Console.warn(msg)     — print to stderr prefixed with "[warn] "
7///   Console.readLine()    — read one line from stdin; Ok(line) or Err("EOF")
8///
9/// All methods require `! [Console]`.
10use std::collections::HashMap;
11
12use crate::value::{RuntimeError, Value, aver_display};
13
14pub fn register(global: &mut HashMap<String, Value>) {
15    let mut members = HashMap::new();
16    for method in &["print", "error", "warn", "readLine"] {
17        members.insert(
18            method.to_string(),
19            Value::Builtin(format!("Console.{}", method)),
20        );
21    }
22    global.insert(
23        "Console".to_string(),
24        Value::Namespace {
25            name: "Console".to_string(),
26            members,
27        },
28    );
29}
30
31pub fn effects(name: &str) -> &'static [&'static str] {
32    match name {
33        "Console.print" => &["Console.print"],
34        "Console.error" => &["Console.error"],
35        "Console.warn" => &["Console.warn"],
36        "Console.readLine" => &["Console.readLine"],
37        _ => &[],
38    }
39}
40
41/// Returns `Some(result)` when `name` is owned by this service, `None` otherwise.
42pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
43    match name {
44        "Console.print" => Some(one_msg(name, args, |s| {
45            println!("{}", s);
46        })),
47        "Console.error" => Some(one_msg(name, args, |s| {
48            eprintln!("{}", s);
49        })),
50        "Console.warn" => Some(one_msg(name, args, |s| {
51            eprintln!("[warn] {}", s);
52        })),
53        "Console.readLine" => Some(read_line(args)),
54        _ => None,
55    }
56}
57
58// ─── Private helpers ──────────────────────────────────────────────────────────
59
60fn one_msg(name: &str, args: &[Value], emit: impl Fn(&str)) -> Result<Value, RuntimeError> {
61    if args.len() != 1 {
62        return Err(RuntimeError::Error(format!(
63            "{}() takes 1 argument, got {}",
64            name,
65            args.len()
66        )));
67    }
68    if let Some(s) = aver_display(&args[0]) {
69        emit(&s);
70    }
71    Ok(Value::Unit)
72}
73
74fn read_line(args: &[Value]) -> Result<Value, RuntimeError> {
75    if !args.is_empty() {
76        return Err(RuntimeError::Error(format!(
77            "Console.readLine() takes 0 arguments, got {}",
78            args.len()
79        )));
80    }
81    match aver_rt::read_line() {
82        Ok(line) => Ok(Value::Ok(Box::new(Value::Str(line)))),
83        Err(e) => Ok(Value::Err(Box::new(Value::Str(e)))),
84    }
85}