1use std::collections::HashMap;
12use std::rc::Rc;
13
14use crate::nan_value::{Arena, NanValue};
15use crate::value::{RuntimeError, Value};
16
17pub fn register(global: &mut HashMap<String, Value>) {
18 let mut members = HashMap::new();
19 for method in &["toHex", "fromHex"] {
20 members.insert(
21 method.to_string(),
22 Value::Builtin(format!("Byte.{}", method)),
23 );
24 }
25 global.insert(
26 "Byte".to_string(),
27 Value::Namespace {
28 name: "Byte".to_string(),
29 members,
30 },
31 );
32}
33
34pub fn effects(_name: &str) -> &'static [&'static str] {
35 &[]
36}
37
38pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
39 match name {
40 "Byte.toHex" => Some(to_hex(args)),
41 "Byte.fromHex" => Some(from_hex(args)),
42 _ => None,
43 }
44}
45
46fn to_hex(args: &[Value]) -> Result<Value, RuntimeError> {
49 if args.len() != 1 {
50 return Err(RuntimeError::Error(format!(
51 "Byte.toHex() takes 1 argument, got {}",
52 args.len()
53 )));
54 }
55 let Value::Int(n) = &args[0] else {
56 return Err(RuntimeError::Error(
57 "Byte.toHex: argument must be an Int".to_string(),
58 ));
59 };
60 let n = *n;
61 if !(0..=255).contains(&n) {
62 return Ok(Value::Err(Box::new(Value::Str(format!(
63 "Byte.toHex: {} is out of range 0–255",
64 n
65 )))));
66 }
67 Ok(Value::Ok(Box::new(Value::Str(format!("{:02x}", n)))))
68}
69
70fn from_hex(args: &[Value]) -> Result<Value, RuntimeError> {
71 if args.len() != 1 {
72 return Err(RuntimeError::Error(format!(
73 "Byte.fromHex() takes 1 argument, got {}",
74 args.len()
75 )));
76 }
77 let Value::Str(s) = &args[0] else {
78 return Err(RuntimeError::Error(
79 "Byte.fromHex: argument must be a String".to_string(),
80 ));
81 };
82 if s.len() != 2 {
83 return Ok(Value::Err(Box::new(Value::Str(format!(
84 "Byte.fromHex: expected exactly 2 hex chars, got '{}'",
85 s
86 )))));
87 }
88 match u8::from_str_radix(s, 16) {
89 Ok(n) => Ok(Value::Ok(Box::new(Value::Int(n as i64)))),
90 Err(_) => Ok(Value::Err(Box::new(Value::Str(format!(
91 "Byte.fromHex: invalid hex '{}'",
92 s
93 ))))),
94 }
95}
96
97pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
100 let methods = &["toHex", "fromHex"];
101 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
102 for method in methods {
103 let idx = arena.push_builtin(&format!("Byte.{}", method));
104 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
105 }
106 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
107 name: Rc::from("Byte"),
108 members,
109 });
110 global.insert("Byte".to_string(), NanValue::new_namespace(ns_idx));
111}
112
113pub fn call_nv(
114 name: &str,
115 args: &[NanValue],
116 arena: &mut Arena,
117) -> Option<Result<NanValue, RuntimeError>> {
118 match name {
119 "Byte.toHex" => Some(to_hex_nv(args, arena)),
120 "Byte.fromHex" => Some(from_hex_nv(args, arena)),
121 _ => None,
122 }
123}
124
125fn nv_ok_str(s: &str, arena: &mut Arena) -> NanValue {
126 let inner = NanValue::new_string_value(s, arena);
127 NanValue::new_ok_value(inner, arena)
128}
129
130fn nv_err_str(s: &str, arena: &mut Arena) -> NanValue {
131 let inner = NanValue::new_string_value(s, arena);
132 NanValue::new_err_value(inner, arena)
133}
134
135fn to_hex_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
136 if args.len() != 1 {
137 return Err(RuntimeError::Error(format!(
138 "Byte.toHex() takes 1 argument, got {}",
139 args.len()
140 )));
141 }
142 if !args[0].is_int() {
143 return Err(RuntimeError::Error(
144 "Byte.toHex: argument must be an Int".to_string(),
145 ));
146 }
147 let n = args[0].as_int(arena);
148 if !(0..=255).contains(&n) {
149 return Ok(nv_err_str(
150 &format!("Byte.toHex: {} is out of range 0-255", n),
151 arena,
152 ));
153 }
154 Ok(nv_ok_str(&format!("{:02x}", n), arena))
155}
156
157fn from_hex_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
158 if args.len() != 1 {
159 return Err(RuntimeError::Error(format!(
160 "Byte.fromHex() takes 1 argument, got {}",
161 args.len()
162 )));
163 }
164 if !args[0].is_string() {
165 return Err(RuntimeError::Error(
166 "Byte.fromHex: argument must be a String".to_string(),
167 ));
168 }
169 let s = arena.get_string_value(args[0]).to_string();
170 if s.len() != 2 {
171 return Ok(nv_err_str(
172 &format!("Byte.fromHex: expected exactly 2 hex chars, got '{}'", s),
173 arena,
174 ));
175 }
176 match u8::from_str_radix(&s, 16) {
177 Ok(n) => {
178 let inner = NanValue::new_int(n as i64, arena);
179 Ok(NanValue::new_ok_value(inner, arena))
180 }
181 Err(_) => Ok(nv_err_str(
182 &format!("Byte.fromHex: invalid hex '{}'", s),
183 arena,
184 )),
185 }
186}