Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 92 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions rad/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ tokio = "1.44.1"

[dependencies]
anyhow = "1.0.98"
base64 = "0.22.1"
bs58 = "0.5.1"
cbor-codec = { git = "https://github.com/witnet/cbor-codec.git", branch = "feat/ldexpf-shim" }
futures = "0.3.31"
hex = "0.4.1"
if_rust_version = "1.0.0"
jsonpath = "0.1.1"
# the http crate is used to perform additional validations before passing arguments to the surf http client
# the version of http must be kept in sync with the version used by surf
http = "0.2.1"
Expand All @@ -26,10 +29,12 @@ minidom = { git = "https://github.com/witnet/xmpp-rs", rev = "bc8a33ff5da95ee403
num_enum = "0.7.3"
ordered-float = "3.9.2"
rand = "0.7.3"
regex = "1.4.2"
reqwest = { version = "0.12.15", features = ["socks"] }
serde = "1.0.111"
serde_cbor = "0.11.2"
serde_json = "1.0.96"
slicestring = "0.3.2"
thiserror = "2.0.12"
# the url crate is used to perform additional validations before passing arguments to the surf http client
# the version of url must be kept in sync with the version used by surf in the `witnet_net` crate
Expand Down
6 changes: 6 additions & 0 deletions rad/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ pub enum RadError {
/// Failed to parse an object from a JSON buffer
#[error("Failed to parse an object from a JSON buffer: {description:?}")]
JsonParse { description: String },
/// The given JSON path is not present in a JSON-stringified object
#[error("Failed to find JSON path `{path}` from RadonString")]
JsonPathNotFound { path: String },
/// Failed to parse a JSON path selector from a string value
#[error("Failed to parse a JSON path from a string value: {description:?}")]
JsonPathParse { description: String },
/// Failed to parse an object from a XML buffer
#[error("Failed to parse an object from a XML buffer: {description:?}")]
XmlParse { description: String },
Expand Down
82 changes: 78 additions & 4 deletions rad/src/operators/bytes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
use base64::Engine;
use serde_cbor::value::{Value, from_value};
use std::convert::TryFrom;

use crate::{
error::RadError,
hash_functions::{self, RadonHashFunctions},
types::{RadonType, bytes::RadonBytes, string::RadonString},
types::{
RadonType,
bytes::{RadonBytes, RadonBytesEncoding},
integer::RadonInteger,
string::RadonString,
},
};

pub fn to_string(input: &RadonBytes) -> Result<RadonString, RadError> {
RadonString::try_from(Value::Text(hex::encode(input.value())))
pub fn as_integer(input: &RadonBytes) -> Result<RadonInteger, RadError> {
let input_value_len = input.value().len();
match input_value_len {
1..=16 => {
let mut bytes_array = [0u8; 16];
bytes_array[16 - input_value_len..].copy_from_slice(&input.value());
Ok(RadonInteger::from(i128::from_be_bytes(bytes_array)))
}
17.. => Err(RadError::ParseInt {
message: "Input buffer too big".to_string(),
}),
_ => Err(RadError::EmptyArray),
}
}

pub fn hash(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError> {
Expand All @@ -27,14 +44,71 @@ pub fn hash(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError>

Ok(RadonBytes::from(digest))
}

pub fn length(input: &RadonBytes) -> RadonInteger {
RadonInteger::from(input.value().len() as i128)
}

pub fn slice(input: &RadonBytes, args: &[Value]) -> Result<RadonBytes, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonString::radon_type_name(),
operator: "BytesSlice".to_string(),
args: args.to_vec(),
};
let end_index = input.value().len();
if end_index > 0 {
let start_index = from_value::<i64>(args[0].clone())
.unwrap_or_default()
.rem_euclid(end_index as i64) as usize;
let mut slice = input.value().as_slice().split_at(start_index).1.to_vec();
if args.len() == 2 {
let end_index = from_value::<i64>(args[1].clone())
.unwrap_or_default()
.rem_euclid(end_index as i64) as usize;
slice.truncate(end_index - start_index);
}
Ok(RadonBytes::from(slice))
} else {
Err(wrong_args())
}
}

pub fn to_string(input: &RadonBytes, args: &Option<Vec<Value>>) -> Result<RadonString, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonString::radon_type_name(),
operator: "Stringify".to_string(),
args: args.to_owned().unwrap_or_default().to_vec(),
};
let mut bytes_encoding = RadonBytesEncoding::Hex;
if let Some(args) = args {
if !args.is_empty() {
let arg = args.first().ok_or_else(wrong_args)?.to_owned();
let bytes_encoding_u8 = from_value::<u8>(arg).map_err(|_| wrong_args())?;
bytes_encoding =
RadonBytesEncoding::try_from(bytes_encoding_u8).map_err(|_| wrong_args())?;
}
}
match bytes_encoding {
RadonBytesEncoding::Hex => RadonString::try_from(Value::Text(hex::encode(input.value()))),
RadonBytesEncoding::Base58 => RadonString::try_from(Value::Text(bs58::encode(input.value()).into_string())),
RadonBytesEncoding::Base64 => RadonString::try_from(Value::Text(
base64::engine::general_purpose::STANDARD.encode(input.value()),
)),
RadonBytesEncoding::Utf8 => Ok(RadonString::from(
String::from_utf8(input.value().to_vec()).unwrap_or_default(),
)),
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_bytes_to_string() {
let input = RadonBytes::from(vec![0x01, 0x02, 0x03]);
let output = to_string(&input).unwrap().value();
let valid_args = Some(vec![Value::from(0x00)]);
let output = to_string(&input, &valid_args).unwrap().value();

let valid_expected = "010203".to_string();

Expand Down
11 changes: 9 additions & 2 deletions rad/src/operators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,12 @@ pub enum RadonOpCodes {
BooleanNegate = 0x22,
///////////////////////////////////////////////////////////////////////
// Bytes operator codes (start at 0x30)
BytesAsString = 0x30,
BytesToString = 0x30,
BytesHash = 0x31,
BytesAsInteger = 0x32,
BytesLength = 0x34,
BytesSlice = 0x3C,

///////////////////////////////////////////////////////////////////////
// Integer operator codes (start at 0x40)
IntegerAbsolute = 0x40,
Expand Down Expand Up @@ -96,7 +100,7 @@ pub enum RadonOpCodes {
///////////////////////////////////////////////////////////////////////
// String operator codes (start at 0x70)
StringAsBoolean = 0x70,
// StringAsBytes = 0x71,
StringAsBytes = 0x71,
StringAsFloat = 0x72,
StringAsInteger = 0x73,
StringLength = 0x74,
Expand All @@ -106,6 +110,9 @@ pub enum RadonOpCodes {
StringParseXMLMap = 0x78,
StringToLowerCase = 0x79,
StringToUpperCase = 0x7A,
StringReplace = 0x7B,
StringSlice = 0x7C,
StringSplit = 0x7D,
}

impl fmt::Display for RadonOpCodes {
Expand Down
Loading
Loading