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 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}