Skip to content

Commit aae16d2

Browse files
committed
feat: implement RadonOpCodes::MapAlter
1 parent 3ce8d2d commit aae16d2

File tree

5 files changed

+112
-32
lines changed

5 files changed

+112
-32
lines changed

rad/src/operators/array.rs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
use std::{
22
clone::Clone,
3-
convert::{TryFrom, TryInto},
4-
iter,
3+
convert::{TryFrom, TryInto}
54
};
65

76
use serde_cbor::value::{from_value, Value};
8-
use witnet_data_structures::radon_report::{RadonReport, ReportContext, Stage};
7+
use witnet_data_structures::radon_report::ReportContext;
98

109
use crate::{
1110
error::RadError,
1211
filters::{self, RadonFilters},
13-
operators::{string, RadonOpCodes},
12+
operators::string,
1413
reducers::{self, RadonReducers},
15-
script::{execute_radon_script, unpack_subscript, RadonCall, RadonScriptExecutionSettings},
14+
script::{execute_radon_script, unpack_subscript, RadonScriptExecutionSettings, partial_results_extract},
1615
types::{array::RadonArray, integer::RadonInteger, string::RadonString, RadonType, RadonTypes},
1716
};
1817

@@ -379,27 +378,6 @@ pub fn sort(
379378
Ok(RadonArray::from(result).into())
380379
}
381380

382-
fn partial_results_extract(
383-
subscript: &[RadonCall],
384-
reports: &[RadonReport<RadonTypes>],
385-
context: &mut ReportContext<RadonTypes>,
386-
) {
387-
if let Stage::Retrieval(metadata) = &mut context.stage {
388-
metadata.subscript_partial_results.push(subscript.iter().chain(iter::once(&(RadonOpCodes::Fail, None))).enumerate().map(|(index, _)|
389-
reports
390-
.iter()
391-
.map(|report|
392-
report.partial_results
393-
.as_ref()
394-
.expect("Execution reports from applying subscripts are expected to contain partial results")
395-
.get(index)
396-
.expect("Execution reports from applying same subscript on multiple values should contain the same number of partial results")
397-
.clone()
398-
).collect::<Vec<RadonTypes>>()
399-
).collect::<Vec<Vec<RadonTypes>>>());
400-
}
401-
}
402-
403381
pub fn transpose(input: &RadonArray) -> Result<RadonArray, RadError> {
404382
let mut v = vec![];
405383
let mut prev_len = None;
@@ -486,15 +464,15 @@ pub mod legacy {
486464
mod tests {
487465
use std::collections::BTreeMap;
488466

489-
use witnet_data_structures::radon_report::RetrievalMetadata;
467+
use witnet_data_structures::radon_report::{RetrievalMetadata, Stage};
490468

491469
use crate::{
492470
error::RadError,
493471
operators::{
494472
Operable,
495473
RadonOpCodes::{
496474
IntegerGreaterThan, IntegerMultiply, MapGetBoolean, MapGetFloat, MapGetInteger,
497-
MapGetString,
475+
MapGetString, self,
498476
},
499477
},
500478
types::{

rad/src/operators/map.rs

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,87 @@
1-
use std::convert::TryInto;
1+
use std::{convert::TryInto, collections::BTreeMap};
22

33
use serde_cbor::value::{from_value, Value};
4+
use witnet_data_structures::radon_report::ReportContext;
45

56
use crate::{
67
error::RadError,
78
operators::string,
8-
types::{array::RadonArray, map::RadonMap, string::RadonString, RadonType, RadonTypes},
9+
types::{array::RadonArray, map::RadonMap, string::RadonString, RadonType, RadonTypes},
10+
script::{RadonScriptExecutionSettings, execute_radon_script, unpack_subscript, partial_results_extract},
911
};
1012

13+
pub fn alter(
14+
input: &RadonMap,
15+
args: &[Value],
16+
context: &mut ReportContext<RadonTypes>
17+
) -> Result<RadonMap, RadError> {
18+
let wrong_args = || RadError::WrongArguments {
19+
input_type: RadonMap::radon_type_name(),
20+
operator: "Alter".to_string(),
21+
args: args.to_vec(),
22+
};
23+
24+
let first_arg = args.get(0).ok_or_else(wrong_args)?;
25+
let mut input_keys = vec![];
26+
match first_arg {
27+
Value::Array(keys) => {
28+
for key in keys.iter() {
29+
let key_string = from_value::<String>(key.to_owned()).map_err(|_| wrong_args())?;
30+
input_keys.push(key_string);
31+
}
32+
}
33+
Value::Text(key) => {
34+
input_keys.push(key.clone());
35+
}
36+
_ => return Err(wrong_args())
37+
};
38+
39+
let subscript = args.get(1).ok_or_else(wrong_args)?;
40+
match subscript {
41+
Value::Array(_arg) => {
42+
let subscript_err = |e| RadError::Subscript {
43+
input_type: "RadonMap".to_string(),
44+
operator: "Alter".to_string(),
45+
inner: Box::new(e),
46+
};
47+
let subscript = unpack_subscript(subscript).map_err(subscript_err)?;
48+
49+
let not_found = |key_str: &str| RadError::MapKeyNotFound { key: String::from(key_str) };
50+
51+
let input_map = input.value();
52+
let mut output_map = input.value().clone();
53+
let mut reports = vec![];
54+
55+
let settings = RadonScriptExecutionSettings::tailored_to_stage(&context.stage);
56+
for key in input_keys {
57+
let value = input_map
58+
.get(key.as_str())
59+
.ok_or_else(|| not_found(key.as_str()))?;
60+
let report = execute_radon_script(
61+
value.clone(),
62+
subscript.as_slice(),
63+
context,
64+
settings
65+
)?;
66+
// If there is an error while altering value, short-circuit and bubble up the error as it comes
67+
// from the radon script execution
68+
if let RadonTypes::RadonError(error) = &report.result {
69+
return Err(error.clone().into_inner());
70+
} else {
71+
output_map.insert(key, report.result.clone());
72+
}
73+
reports.push(report);
74+
}
75+
76+
// Extract the partial results from the reports and put them in the execution context if needed
77+
partial_results_extract(&subscript, &reports, context);
78+
79+
Ok(RadonMap::from(output_map))
80+
}
81+
_ => Err(wrong_args())
82+
}
83+
}
84+
1185
fn inner_get(input: &RadonMap, args: &[Value]) -> Result<RadonTypes, RadError> {
1286
let wrong_args = || RadError::WrongArguments {
1387
input_type: RadonMap::radon_type_name(),

rad/src/operators/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ pub enum RadonOpCodes {
9494
MapGetString = 0x67,
9595
MapKeys = 0x68,
9696
MapValues = 0x69,
97+
//MapEntries = 0x6A,
98+
MapAlter = 0x6B,
9799
///////////////////////////////////////////////////////////////////////
98100
// String operator codes (start at 0x70)
99101
StringAsBoolean = 0x70,

rad/src/script.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use std::convert::TryFrom;
1+
use std::{
2+
clone::Clone,
3+
convert::TryFrom,
4+
iter,
5+
};
26

37
use serde_cbor::{
48
self as cbor,
@@ -280,6 +284,27 @@ pub fn create_radon_script_from_filters_and_reducer(
280284
Ok(radoncall_vec)
281285
}
282286

287+
pub fn partial_results_extract(
288+
subscript: &[RadonCall],
289+
reports: &[RadonReport<RadonTypes>],
290+
context: &mut ReportContext<RadonTypes>,
291+
) {
292+
if let Stage::Retrieval(metadata) = &mut context.stage {
293+
metadata.subscript_partial_results.push(subscript.iter().chain(iter::once(&(RadonOpCodes::Fail, None))).enumerate().map(|(index, _)|
294+
reports
295+
.iter()
296+
.map(|report|
297+
report.partial_results
298+
.as_ref()
299+
.expect("Execution reports from applying subscripts are expected to contain partial results")
300+
.get(index)
301+
.expect("Execution reports from applying same subscript on multiple values should contain the same number of partial results")
302+
.clone()
303+
).collect::<Vec<RadonTypes>>()
304+
).collect::<Vec<Vec<RadonTypes>>>());
305+
}
306+
}
307+
283308
#[cfg(test)]
284309
mod tests {
285310
use std::collections::BTreeMap;
@@ -299,7 +324,7 @@ mod tests {
299324
r#"{"coord":{"lon":13.41,"lat":52.52},"weather":[{"id":600,"main":"Snow","description":"light snow","icon":"13n"}],"base":"stations","main":{"temp":-4,"pressure":1013,"humidity":73,"temp_min":-4,"temp_max":-4},"visibility":10000,"wind":{"speed":2.6,"deg":90},"clouds":{"all":75},"dt":1548346800,"sys":{"type":1,"id":1275,"message":0.0038,"country":"DE","sunrise":1548313160,"sunset":1548344298},"id":2950159,"name":"Berlin","cod":200}"#,
300325
));
301326
let script = vec![
302-
(RadonOpCodes::StringParseJSONMap, None),
327+
(RadonOpCodes::StringParseJSONMap, Some(vec![])),
303328
(
304329
RadonOpCodes::MapGetMap,
305330
Some(vec![Value::Text(String::from("main"))]),

rad/src/types/map.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ impl Operable for RadonMap {
161161
}
162162
(RadonOpCodes::MapKeys, None) => Ok(RadonTypes::from(map_operators::keys(self))),
163163
(RadonOpCodes::MapValues, None) => Ok(RadonTypes::from(map_operators::values(self))),
164+
(RadonOpCodes::MapAlter, Some(args)) => map_operators::alter(self, args, context).map(RadonTypes::from),
164165
(RadonOpCodes::MapStringify, None) => map_operators::stringify(self).map(RadonTypes::from),
165166
(op_code, args) => Err(RadError::UnsupportedOperator {
166167
input_type: RADON_MAP_TYPE_NAME.to_string(),

0 commit comments

Comments
 (0)