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 s_idx = arena.push_string(s);
127 let inner = NanValue::new_string(s_idx);
128 let box_idx = arena.push_boxed(inner);
129 NanValue::new_ok(box_idx)
130}
131
132fn nv_err_str(s: &str, arena: &mut Arena) -> NanValue {
133 let s_idx = arena.push_string(s);
134 let inner = NanValue::new_string(s_idx);
135 let box_idx = arena.push_boxed(inner);
136 NanValue::new_err(box_idx)
137}
138
139fn to_hex_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
140 if args.len() != 1 {
141 return Err(RuntimeError::Error(format!(
142 "Byte.toHex() takes 1 argument, got {}",
143 args.len()
144 )));
145 }
146 if !args[0].is_int() {
147 return Err(RuntimeError::Error(
148 "Byte.toHex: argument must be an Int".to_string(),
149 ));
150 }
151 let n = args[0].as_int(arena);
152 if !(0..=255).contains(&n) {
153 return Ok(nv_err_str(
154 &format!("Byte.toHex: {} is out of range 0-255", n),
155 arena,
156 ));
157 }
158 Ok(nv_ok_str(&format!("{:02x}", n), arena))
159}
160
161fn from_hex_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
162 if args.len() != 1 {
163 return Err(RuntimeError::Error(format!(
164 "Byte.fromHex() takes 1 argument, got {}",
165 args.len()
166 )));
167 }
168 if !args[0].is_string() {
169 return Err(RuntimeError::Error(
170 "Byte.fromHex: argument must be a String".to_string(),
171 ));
172 }
173 let s = arena.get_string(args[0].arena_index()).to_string();
174 if s.len() != 2 {
175 return Ok(nv_err_str(
176 &format!("Byte.fromHex: expected exactly 2 hex chars, got '{}'", s),
177 arena,
178 ));
179 }
180 match u8::from_str_radix(&s, 16) {
181 Ok(n) => {
182 let inner = NanValue::new_int(n as i64, arena);
183 let box_idx = arena.push_boxed(inner);
184 Ok(NanValue::new_ok(box_idx))
185 }
186 Err(_) => Ok(nv_err_str(
187 &format!("Byte.fromHex: invalid hex '{}'", s),
188 arena,
189 )),
190 }
191}