1use std::{borrow::Borrow, ops::Deref};
2
3use jrsonnet_evaluator::{
4 function::builtin,
5 runtime_error,
6 typed::{CheckType, ComplexValType, Typed, ValType},
7 Result, Val,
8};
9
10use crate::ensure;
11
12#[derive(Eq, Ord, PartialEq, PartialOrd)]
13#[repr(transparent)]
14pub struct Hex(pub Vec<u8>);
15impl Hex {
16 pub fn encode_str(str: &str) -> Self {
17 Self(str.as_bytes().into())
18 }
19}
20impl Typed for Hex {
21 const TYPE: &'static ComplexValType = &ComplexValType::Simple(ValType::Str);
22
23 fn into_untyped(typed: Self) -> Result<Val> {
24 Ok(Val::Str(to_hex(&typed.0).into()))
25 }
26
27 fn from_untyped(untyped: Val) -> Result<Self> {
28 Self::TYPE.check(&untyped)?;
29 let str = untyped.as_str().expect("is string");
30 Ok(Self(from_hex(&str)?))
31 }
32}
33impl Borrow<[u8]> for Hex {
34 fn borrow(&self) -> &[u8] {
35 &self.0
36 }
37}
38impl Deref for Hex {
39 type Target = [u8];
40
41 fn deref(&self) -> &Self::Target {
42 self.0.as_slice()
43 }
44}
45
46pub fn to_hex(data: &[u8]) -> String {
48 let mut out = vec![0; data.len() * 2 + 2];
49 out[0] = b'0';
50 out[1] = b'x';
51 hex::encode_to_slice(data, &mut out[2..]).expect("size is correct");
52 String::from_utf8(out).expect("hex is utf-8 compatible")
53}
54
55pub fn from_hex(data: &str) -> Result<Vec<u8>> {
57 ensure!(data.starts_with("0x"), "string doesn't starts with 0x");
58 let out = hex::decode(&data.as_bytes()[2..])
59 .map_err(|e| runtime_error!("failed to decode hex: {e}"))?;
60 Ok(out)
61}
62
63#[builtin]
73pub fn builtin_to_hex(data: Vec<u8>) -> Result<Hex> {
74 Ok(Hex(data))
75}
76
77#[builtin]
87pub fn builtin_from_hex(data: Hex) -> Vec<u8> {
88 data.0
89}