1use std::collections::HashMap;
11use std::rc::Rc;
12
13use crate::nan_value::{Arena, NanValue};
14use crate::value::{RuntimeError, Value, aver_display};
15
16pub fn register(global: &mut HashMap<String, Value>) {
17 let mut members = HashMap::new();
18 for method in &["print", "error", "warn", "readLine"] {
19 members.insert(
20 method.to_string(),
21 Value::Builtin(format!("Console.{}", method)),
22 );
23 }
24 global.insert(
25 "Console".to_string(),
26 Value::Namespace {
27 name: "Console".to_string(),
28 members,
29 },
30 );
31}
32
33pub fn effects(name: &str) -> &'static [&'static str] {
34 match name {
35 "Console.print" => &["Console.print"],
36 "Console.error" => &["Console.error"],
37 "Console.warn" => &["Console.warn"],
38 "Console.readLine" => &["Console.readLine"],
39 _ => &[],
40 }
41}
42
43pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
45 match name {
46 "Console.print" => Some(one_msg(name, args, |s| {
47 println!("{}", s);
48 })),
49 "Console.error" => Some(one_msg(name, args, |s| {
50 eprintln!("{}", s);
51 })),
52 "Console.warn" => Some(one_msg(name, args, |s| {
53 eprintln!("[warn] {}", s);
54 })),
55 "Console.readLine" => Some(read_line(args)),
56 _ => None,
57 }
58}
59
60fn one_msg(name: &str, args: &[Value], emit: impl Fn(&str)) -> Result<Value, RuntimeError> {
63 if args.len() != 1 {
64 return Err(RuntimeError::Error(format!(
65 "{}() takes 1 argument, got {}",
66 name,
67 args.len()
68 )));
69 }
70 if let Some(s) = aver_display(&args[0]) {
71 emit(&s);
72 }
73 Ok(Value::Unit)
74}
75
76fn read_line(args: &[Value]) -> Result<Value, RuntimeError> {
77 if !args.is_empty() {
78 return Err(RuntimeError::Error(format!(
79 "Console.readLine() takes 0 arguments, got {}",
80 args.len()
81 )));
82 }
83 match aver_rt::read_line() {
84 Ok(line) => Ok(Value::Ok(Box::new(Value::Str(line)))),
85 Err(e) => Ok(Value::Err(Box::new(Value::Str(e)))),
86 }
87}
88
89pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
92 let methods = &["print", "error", "warn", "readLine"];
93 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
94 for method in methods {
95 let idx = arena.push_builtin(&format!("Console.{}", method));
96 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
97 }
98 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
99 name: Rc::from("Console"),
100 members,
101 });
102 global.insert("Console".to_string(), NanValue::new_namespace(ns_idx));
103}
104
105pub fn call_nv(
106 name: &str,
107 args: &[NanValue],
108 arena: &mut Arena,
109) -> Option<Result<NanValue, RuntimeError>> {
110 match name {
111 "Console.print" => Some(one_msg_nv(name, args, arena, |s| {
112 println!("{}", s);
113 })),
114 "Console.error" => Some(one_msg_nv(name, args, arena, |s| {
115 eprintln!("{}", s);
116 })),
117 "Console.warn" => Some(one_msg_nv(name, args, arena, |s| {
118 eprintln!("[warn] {}", s);
119 })),
120 "Console.readLine" => Some(read_line_nv(args, arena)),
121 _ => None,
122 }
123}
124
125fn one_msg_nv(
126 name: &str,
127 args: &[NanValue],
128 arena: &mut Arena,
129 emit: impl Fn(&str),
130) -> Result<NanValue, RuntimeError> {
131 if args.len() != 1 {
132 return Err(RuntimeError::Error(format!(
133 "{}() takes 1 argument, got {}",
134 name,
135 args.len()
136 )));
137 }
138 if let Some(s) = args[0].display(arena) {
139 emit(&s);
140 }
141 Ok(NanValue::UNIT)
142}
143
144fn read_line_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
145 if !args.is_empty() {
146 return Err(RuntimeError::Error(format!(
147 "Console.readLine() takes 0 arguments, got {}",
148 args.len()
149 )));
150 }
151 match aver_rt::read_line() {
152 Ok(line) => {
153 let inner = NanValue::new_string_value(&line, arena);
154 Ok(NanValue::new_ok_value(inner, arena))
155 }
156 Err(e) => {
157 let inner = NanValue::new_string_value(&e, arena);
158 Ok(NanValue::new_err_value(inner, arena))
159 }
160 }
161}