From ec7f659539315ddcdd338b760dc3122d70921054 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sun, 4 Aug 2024 14:29:05 +0200 Subject: [PATCH] Allow bytes to be defined as plain strings, or with specified encoding. The syntax is as follows: { "bytes" = "...", "encoding" = "" } The following encoding are accepted: "utf8", "utf-8", "hex", "base16" Note: the duplicates are only there to make it easier for people to discover them by accident. When "hex" (resp. "base16") is specified, the bytes string will be decoded and must be a valid hex string. --- crates/aiken-project/src/config.rs | 83 ++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 11 deletions(-) diff --git a/crates/aiken-project/src/config.rs b/crates/aiken-project/src/config.rs index 90993bb2..46464816 100644 --- a/crates/aiken-project/src/config.rs +++ b/crates/aiken-project/src/config.rs @@ -11,7 +11,7 @@ use miette::NamedSource; use semver::Version; use serde::{ de, - ser::{self, SerializeSeq}, + ser::{self, SerializeSeq, SerializeStruct}, Deserialize, Serialize, }; use std::{collections::BTreeMap, fmt::Display, fs, io, path::Path}; @@ -42,7 +42,7 @@ pub struct Config { pub enum SimpleExpr { Int(i64), Bool(bool), - ByteArray(String), + ByteArray(Vec, ByteArrayFormatPreference), List(Vec), } @@ -60,10 +60,10 @@ impl SimpleExpr { numeric_underscore: false, }, }, - SimpleExpr::ByteArray(s) => UntypedExpr::ByteArray { + SimpleExpr::ByteArray(bs, preferred_format) => UntypedExpr::ByteArray { location: Span::empty(), - bytes: s.as_bytes().to_vec(), - preferred_format: ByteArrayFormatPreference::Utf8String, + bytes: bs.to_vec(), + preferred_format: *preferred_format, }, SimpleExpr::List(es) => UntypedExpr::List { location: Span::empty(), @@ -89,12 +89,12 @@ impl SimpleExpr { }, Some(Annotation::int(location)), ), - SimpleExpr::ByteArray(s) => ( + SimpleExpr::ByteArray(bs, preferred_format) => ( // TODO: Replace with 'self.as_untyped_expr()' after https://github.com/aiken-lang/aiken/pull/992 Constant::ByteArray { location, - bytes: s.as_bytes().to_vec(), - preferred_format: ByteArrayFormatPreference::Utf8String, + bytes: bs.to_vec(), + preferred_format: *preferred_format, }, Some(Annotation::bytearray(location)), ), @@ -118,7 +118,18 @@ impl Serialize for SimpleExpr { match self { SimpleExpr::Bool(b) => serializer.serialize_bool(*b), SimpleExpr::Int(i) => serializer.serialize_i64(*i), - SimpleExpr::ByteArray(s) => serializer.serialize_str(s.as_str()), + SimpleExpr::ByteArray(bs, preferred_format) => match preferred_format { + ByteArrayFormatPreference::Utf8String => { + serializer.serialize_str(String::from_utf8(bs.to_vec()).unwrap().as_str()) + } + ByteArrayFormatPreference::ArrayOfBytes(..) + | ByteArrayFormatPreference::HexadecimalString => { + let mut s = serializer.serialize_struct("ByteArray", 2)?; + s.serialize_field("bytes", &hex::encode(bs))?; + s.serialize_field("encoding", "base16")?; + s.end() + } + }, SimpleExpr::List(es) => { let mut seq = serializer.serialize_seq(Some(es.len()))?; for e in es { @@ -134,6 +145,24 @@ impl<'a> Deserialize<'a> for SimpleExpr { fn deserialize>(deserializer: D) -> Result { struct SimpleExprVisitor; + #[derive(Deserialize)] + enum Encoding { + #[serde(rename(deserialize = "utf8"))] + Utf8, + #[serde(rename(deserialize = "utf-8"))] + Utf8Bis, + #[serde(rename(deserialize = "hex"))] + Hex, + #[serde(rename(deserialize = "base16"))] + Base16, + } + + #[derive(Deserialize)] + struct Bytes { + bytes: String, + encoding: Encoding, + } + impl<'a> de::Visitor<'a> for SimpleExprVisitor { type Value = SimpleExpr; @@ -150,7 +179,32 @@ impl<'a> Deserialize<'a> for SimpleExpr { } fn visit_str(self, s: &str) -> Result { - Ok(SimpleExpr::ByteArray(s.to_string())) + Ok(SimpleExpr::ByteArray( + s.as_bytes().to_vec(), + ByteArrayFormatPreference::Utf8String, + )) + } + + fn visit_map(self, map: V) -> Result + where + V: de::MapAccess<'a>, + { + let Bytes { bytes, encoding } = + Bytes::deserialize(de::value::MapAccessDeserializer::new(map))?; + + match encoding { + Encoding::Hex | Encoding::Base16 => match hex::decode(&bytes) { + Err(e) => Err(de::Error::custom(format!("invalid base16 string: {e:?}"))), + Ok(bytes) => Ok(SimpleExpr::ByteArray( + bytes, + ByteArrayFormatPreference::HexadecimalString, + )), + }, + Encoding::Utf8 | Encoding::Utf8Bis => Ok(SimpleExpr::ByteArray( + bytes.as_bytes().to_vec(), + ByteArrayFormatPreference::Utf8String, + )), + } } fn visit_seq(self, mut seq: A) -> Result @@ -335,7 +389,14 @@ mod tests { let leaf = prop_oneof![ (any::)().prop_map(SimpleExpr::Int), (any::)().prop_map(SimpleExpr::Bool), - "[a-z]*".prop_map(SimpleExpr::ByteArray) + "[a-z0-9]*".prop_map(|bytes| SimpleExpr::ByteArray( + bytes.as_bytes().to_vec(), + ByteArrayFormatPreference::Utf8String + )), + "([0-9a-f][0-9a-f])*".prop_map(|bytes| SimpleExpr::ByteArray( + bytes.as_bytes().to_vec(), + ByteArrayFormatPreference::HexadecimalString + )) ]; leaf.prop_recursive(3, 8, 3, |inner| {