--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs +++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs @@ -1,5 +1,5 @@ use crate::function::StaticBuiltin; -use crate::typed::{Any, PositiveF64, VecVal, M1}; +use crate::typed::{Any, Bytes, PositiveF64, VecVal, M1}; use crate::{ builtin::manifest::{manifest_yaml_ex, ManifestYamlOptions}, equals, @@ -449,17 +449,15 @@ } #[jrsonnet_macros::builtin] -fn builtin_encode_utf8(str: IStr) -> Result { - Ok(VecVal( - str.bytes() - .map(|b| Val::Num(b as f64)) - .collect::>(), - )) +fn builtin_encode_utf8(str: IStr) -> Result { + Ok(Bytes(str.bytes().map(|b| b).collect::>().into())) } #[jrsonnet_macros::builtin] -fn builtin_decode_utf8(arr: Vec) -> Result { - Ok(String::from_utf8(arr).map_err(|_| RuntimeError("bad utf8".into()))?) +fn builtin_decode_utf8(arr: Bytes) -> Result { + Ok(std::str::from_utf8(&arr.0) + .map_err(|_| RuntimeError("bad utf8".into()))? + .into()) } #[jrsonnet_macros::builtin] @@ -485,17 +483,21 @@ } #[jrsonnet_macros::builtin] -fn builtin_base64(input: Either![Vec, IStr]) -> Result { +fn builtin_base64(input: Either![Bytes, IStr]) -> Result { use Either2::*; Ok(match input { - A(a) => base64::encode(a), + A(a) => base64::encode(a.0), B(l) => base64::encode(l.bytes().collect::>()), }) } #[jrsonnet_macros::builtin] -fn builtin_base64_decode_bytes(input: IStr) -> Result> { - Ok(base64::decode(&input.as_bytes()).map_err(|_| RuntimeError("bad base64".into()))?) +fn builtin_base64_decode_bytes(input: IStr) -> Result { + Ok(Bytes( + base64::decode(&input.as_bytes()) + .map_err(|_| RuntimeError("bad base64".into()))? + .into(), + )) } #[jrsonnet_macros::builtin] --- a/crates/jrsonnet-evaluator/src/typed/conversions.rs +++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs @@ -1,4 +1,7 @@ -use std::convert::{TryFrom, TryInto}; +use std::{ + convert::{TryFrom, TryInto}, + rc::Rc, +}; use gcmodule::Cc; use jrsonnet_interner::IStr; @@ -295,6 +298,44 @@ } } +/// Specialization +pub struct Bytes(pub Rc<[u8]>); + +impl Typed for Bytes { + const TYPE: &'static ComplexValType = + &ComplexValType::ArrayRef(&ComplexValType::BoundedNumber(Some(0.0), Some(255.0))); +} +impl TryFrom for Bytes { + type Error = LocError; + + fn try_from(value: Val) -> Result { + match value { + Val::Arr(ArrValue::Bytes(bytes)) => Ok(Self(bytes)), + _ => { + ::TYPE.check(&value)?; + match value { + Val::Arr(a) => { + let mut out = Vec::with_capacity(a.len()); + for e in a.iter() { + let r = e?; + out.push(u8::try_from(r)?); + } + Ok(Self(out.into())) + } + _ => unreachable!(), + } + } + } + } +} +impl TryFrom for Val { + type Error = LocError; + + fn try_from(value: Bytes) -> Result { + Ok(Val::Arr(ArrValue::Bytes(value.0))) + } +} + pub struct M1; impl Typed for M1 { const TYPE: &'static ComplexValType = &ComplexValType::BoundedNumber(Some(-1.0), Some(-1.0));