Skip to content
Open
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
129 changes: 125 additions & 4 deletions rad/src/operators/map.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,84 @@
use std::convert::TryInto;
use std::{collections::BTreeMap, convert::TryInto};

use serde_cbor::value::{Value, from_value};
use witnet_data_structures::radon_report::ReportContext;

use crate::{
error::RadError,
operators::string,
types::{RadonType, RadonTypes, array::RadonArray, map::RadonMap, string::RadonString},
error::RadError, operators::string, script::{execute_radon_script, partial_results_extract, unpack_subscript, RadonScriptExecutionSettings}, types::{array::RadonArray, map::RadonMap, string::RadonString, RadonType, RadonTypes}
};

pub fn alter(
input: &RadonMap,
args: &[Value],
context: &mut ReportContext<RadonTypes>
) -> Result<RadonMap, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonMap::radon_type_name(),
operator: "Alter".to_string(),
args: args.to_vec(),
};

let first_arg = args.first().ok_or_else(wrong_args)?;
let mut input_keys = vec![];
match first_arg {
Value::Array(keys) => {
for key in keys.iter() {
let key_string = from_value::<String>(key.to_owned()).map_err(|_| wrong_args())?;
input_keys.push(key_string);
}
}
Value::Text(key) => {
input_keys.push(key.clone());
}
_ => return Err(wrong_args())
};

let subscript = args.get(1).ok_or_else(wrong_args)?;
match subscript {
Value::Array(_arg) => {
let subscript_err = |e| RadError::Subscript {
input_type: "RadonMap".to_string(),
operator: "Alter".to_string(),
inner: Box::new(e),
};
let subscript = unpack_subscript(subscript).map_err(subscript_err)?;

let not_found = |key_str: &str| RadError::MapKeyNotFound { key: String::from(key_str) };

let input_map = input.value();
let mut output_map = input.value().clone();
let mut reports = vec![];

let settings = RadonScriptExecutionSettings::tailored_to_stage(&context.stage);
for key in input_keys {
let value = input_map
.get(key.as_str())
.ok_or_else(|| not_found(key.as_str()))?;
let report = execute_radon_script(
value.clone(),
subscript.as_slice(),
context,
settings
)?;
// If there is an error while altering value, short-circuit and bubble up the error as it comes
// from the radon script execution
if let RadonTypes::RadonError(error) = &report.result {
return Err(error.clone().into_inner());
} else {
output_map.insert(key, report.result.clone());
}
reports.push(report);
}

// Extract the partial results from the reports and put them in the execution context if needed
partial_results_extract(&subscript, &reports, context);

Ok(RadonMap::from(output_map))
}
_ => Err(wrong_args())
}
}

fn inner_get(input: &RadonMap, args: &[Value]) -> Result<RadonTypes, RadError> {
let wrong_args = || RadError::WrongArguments {
input_type: RadonMap::radon_type_name(),
Expand Down Expand Up @@ -76,11 +147,61 @@ pub fn keys(input: &RadonMap) -> RadonArray {
RadonArray::from(v)
}

pub fn pick(input: &RadonMap, args: &[Value]) -> Result<RadonMap, RadError> {
let not_found = |key_str: &str| RadError::MapKeyNotFound {
key: String::from(key_str)
};

let wrong_args = || RadError::WrongArguments {
input_type: RadonMap::radon_type_name(),
operator: "Pick".to_string(),
args: args.to_vec(),
};

let mut input_keys = vec![];
if args.len() > 1 {
return Err(wrong_args());
} else {
let first_arg = args.first().ok_or_else(wrong_args)?;
match first_arg {
Value::Array(keys) => {
for key in keys.iter() {
let key_string = from_value::<String>(key.to_owned()).map_err(|_| wrong_args())?;
input_keys.push(key_string);
}
}
Value::Text(key) => {
input_keys.push(key.clone());
}
_ => return Err(wrong_args())
};
}

let mut output_map = BTreeMap::<String, RadonTypes>::default();
for key in input_keys {
if let Some(value) = input.value().get(&key) {
output_map.insert(key, value.clone());
} else {
return Err(not_found(key.as_str()))
}
}
Ok(RadonMap::from(output_map))
}

pub fn values(input: &RadonMap) -> RadonArray {
let v: Vec<RadonTypes> = input.value().values().cloned().collect();
RadonArray::from(v)
}

pub fn to_string(input: &RadonMap) -> Result<RadonString, RadError> {
let json_string = serde_json::to_string(&input.value())
.map_err(|_| RadError::Decode {
from: "RadonMap",
to: "RadonString"
})?;
Ok(RadonString::from(json_string))
}

/// This module was introduced for encapsulating the interim legacy logic before WIP-0024 is
/// introduced, for the sake of maintainability.
///
Expand Down
4 changes: 3 additions & 1 deletion rad/src/operators/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub enum RadonOpCodes {
FloatTruncate = 0x5D,
///////////////////////////////////////////////////////////////////////
// Map operator codes (start at 0x60)
// MapEntries = 0x60,
MapToString = 0x60,
MapGetArray = 0x61,
MapGetBoolean = 0x62,
MapGetBytes = 0x63,
Expand All @@ -93,6 +93,8 @@ pub enum RadonOpCodes {
MapGetString = 0x67,
MapKeys = 0x68,
MapValues = 0x69,
MapAlter = 0x6B,
MapPick = 0x6E,
///////////////////////////////////////////////////////////////////////
// String operator codes (start at 0x70)
StringAsBoolean = 0x70,
Expand Down
27 changes: 26 additions & 1 deletion rad/src/script.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use std::convert::TryFrom;
use std::{
clone::Clone,
convert::TryFrom,
iter,
};

use serde_cbor::{
self as cbor,
Expand Down Expand Up @@ -280,6 +284,27 @@ pub fn create_radon_script_from_filters_and_reducer(
Ok(radoncall_vec)
}

pub fn partial_results_extract(
subscript: &[RadonCall],
reports: &[RadonReport<RadonTypes>],
context: &mut ReportContext<RadonTypes>,
) {
if let Stage::Retrieval(metadata) = &mut context.stage {
metadata.subscript_partial_results.push(subscript.iter().chain(iter::once(&(RadonOpCodes::Fail, None))).enumerate().map(|(index, _)|
reports
.iter()
.map(|report|
report.partial_results
.as_ref()
.expect("Execution reports from applying subscripts are expected to contain partial results")
.get(index)
.expect("Execution reports from applying same subscript on multiple values should contain the same number of partial results")
.clone()
).collect::<Vec<RadonTypes>>()
).collect::<Vec<Vec<RadonTypes>>>());
}
}

#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
Expand Down
4 changes: 4 additions & 0 deletions rad/src/types/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl Operable for RadonMap {

match call {
(RadonOpCodes::Identity, None) => identity(RadonTypes::from(self.clone())),
(RadonOpCodes::MapToString, None) => map_operators::to_string(self).map(RadonTypes::from),
(RadonOpCodes::MapGetArray, Some(args)) => {
map_operators::get::<RadonArray, _>(self, args.as_slice()).map(RadonTypes::from)
}
Expand Down Expand Up @@ -161,6 +162,9 @@ impl Operable for RadonMap {
}
(RadonOpCodes::MapKeys, None) => Ok(RadonTypes::from(map_operators::keys(self))),
(RadonOpCodes::MapValues, None) => Ok(RadonTypes::from(map_operators::values(self))),
(RadonOpCodes::MapAlter, Some(args)) => map_operators::alter(self, args, context).map(RadonTypes::from),
(RadonOpCodes::MapPick, Some(args)) => map_operators::pick(self, args).map(RadonTypes::from),

(op_code, args) => Err(RadError::UnsupportedOperator {
input_type: RADON_MAP_TYPE_NAME.to_string(),
operator: op_code.to_string(),
Expand Down
Loading