Skip to main content

aver/types/
byte.rs

1/// Byte namespace — byte-level operations on integers.
2///
3/// Byte is NOT a type — these are functions operating on Int (0–255).
4/// Same pattern as Char: namespace of operations on existing types.
5///
6/// Methods:
7///   Byte.toHex(n: Int)        → Result<String, String>  — byte to 2-char lowercase hex
8///   Byte.fromHex(s: String)   → Result<Int, String>     — 2-char hex to byte
9///
10/// No effects required.
11use 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
46// ─── Implementations ────────────────────────────────────────────────────────
47
48fn 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
97// ─── NanValue-native API ─────────────────────────────────────────────────────
98
99pub 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}