1use std::collections::HashMap;
12
13use crate::value::{RuntimeError, Value};
14
15pub fn register(global: &mut HashMap<String, Value>) {
16 let mut members = HashMap::new();
17 for method in &["toCode", "fromCode"] {
18 members.insert(
19 method.to_string(),
20 Value::Builtin(format!("Char.{}", method)),
21 );
22 }
23 global.insert(
24 "Char".to_string(),
25 Value::Namespace {
26 name: "Char".to_string(),
27 members,
28 },
29 );
30}
31
32pub fn effects(_name: &str) -> &'static [&'static str] {
33 &[]
34}
35
36pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
37 match name {
38 "Char.toCode" => Some(to_code(args)),
39 "Char.fromCode" => Some(from_code(args)),
40 _ => None,
41 }
42}
43
44fn to_code(args: &[Value]) -> Result<Value, RuntimeError> {
47 if args.len() != 1 {
48 return Err(RuntimeError::Error(format!(
49 "Char.toCode() takes 1 argument, got {}",
50 args.len()
51 )));
52 }
53 let Value::Str(s) = &args[0] else {
54 return Err(RuntimeError::Error(
55 "Char.toCode: argument must be a String".to_string(),
56 ));
57 };
58 match s.chars().next() {
59 Some(c) => Ok(Value::Int(c as i64)),
60 None => Err(RuntimeError::Error(
61 "Char.toCode: string is empty".to_string(),
62 )),
63 }
64}
65
66fn from_code(args: &[Value]) -> Result<Value, RuntimeError> {
67 if args.len() != 1 {
68 return Err(RuntimeError::Error(format!(
69 "Char.fromCode() takes 1 argument, got {}",
70 args.len()
71 )));
72 }
73 let Value::Int(n) = &args[0] else {
74 return Err(RuntimeError::Error(
75 "Char.fromCode: argument must be an Int".to_string(),
76 ));
77 };
78 let n = *n;
79 if n < 0 {
80 return Ok(Value::None);
81 }
82 let Ok(code) = u32::try_from(n) else {
83 return Ok(Value::None);
84 };
85 match char::from_u32(code) {
86 Some(c) => Ok(Value::Some(Box::new(Value::Str(c.to_string())))),
87 None => Ok(Value::None),
88 }
89}