mirror of
https://github.com/fluencelabs/aquavm
synced 2025-04-24 23:02:16 +00:00
Introduce %last_error% (#63)
This commit is contained in:
parent
709b5e0a52
commit
05499a6bc1
@ -44,7 +44,7 @@ Instr: Box<Instruction<'input>> = {
|
||||
! => { errors.push(<>); Box::new(Instruction::Error) },
|
||||
}
|
||||
|
||||
Args: Vec<CallArgValue<'input>> = {
|
||||
Args: Vec<CallInstrArgValue<'input>> = {
|
||||
"[" <args:(<Arg>)*> "]" => args
|
||||
}
|
||||
|
||||
@ -63,19 +63,31 @@ Output: CallOutputValue<'input> = {
|
||||
<a:Accumulator> => CallOutputValue::Accumulator(a),
|
||||
};
|
||||
|
||||
Function = CallArgValue;
|
||||
PeerId = CallArgValue;
|
||||
ServiceId = CallArgValue;
|
||||
Arg = CallArgValue;
|
||||
Function = CallInstrValue;
|
||||
PeerId = CallInstrValue;
|
||||
ServiceId = CallInstrValue;
|
||||
|
||||
CallArgValue: CallArgValue<'input> = {
|
||||
<s:Literal> => CallArgValue::Literal(s),
|
||||
<s:Alphanumeric> => CallArgValue::Variable(s),
|
||||
CallInstrValue: CallInstrValue<'input> = {
|
||||
<s:Literal> => CallInstrValue::Literal(s),
|
||||
<s:Alphanumeric> => CallInstrValue::Variable(s),
|
||||
<v:JsonPath> => {
|
||||
let (variable, path) = into_variable_and_path(v.0, v.1);
|
||||
CallArgValue::JsonPath { variable, path }
|
||||
CallInstrValue::JsonPath { variable, path }
|
||||
},
|
||||
InitPeerId => CallArgValue::InitPeerId,
|
||||
InitPeerId => CallInstrValue::InitPeerId,
|
||||
}
|
||||
|
||||
Arg = CallInstrArgValue;
|
||||
|
||||
CallInstrArgValue: CallInstrArgValue<'input> = {
|
||||
<s:Literal> => CallInstrArgValue::Literal(s),
|
||||
<s:Alphanumeric> => CallInstrArgValue::Variable(s),
|
||||
<v:JsonPath> => {
|
||||
let (variable, path) = into_variable_and_path(v.0, v.1);
|
||||
CallInstrArgValue::JsonPath { variable, path }
|
||||
},
|
||||
InitPeerId => CallInstrArgValue::InitPeerId,
|
||||
LastError => CallInstrArgValue::LastError,
|
||||
}
|
||||
|
||||
Iterable: IterableValue<'input> = {
|
||||
@ -111,6 +123,7 @@ extern {
|
||||
Accumulator => Token::Accumulator(<&'input str>),
|
||||
|
||||
InitPeerId => Token::InitPeerId,
|
||||
LastError => Token::LastError,
|
||||
|
||||
call => Token::Call,
|
||||
seq => Token::Seq,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -36,32 +36,41 @@ pub enum Instruction<'i> {
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||
pub enum PeerPart<'i> {
|
||||
PeerPk(CallArgValue<'i>),
|
||||
PeerPkWithServiceId(CallArgValue<'i>, CallArgValue<'i>),
|
||||
PeerPk(CallInstrValue<'i>),
|
||||
PeerPkWithServiceId(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||
pub enum FunctionPart<'i> {
|
||||
FuncName(CallArgValue<'i>),
|
||||
ServiceIdWithFuncName(CallArgValue<'i>, CallArgValue<'i>),
|
||||
FuncName(CallInstrValue<'i>),
|
||||
ServiceIdWithFuncName(CallInstrValue<'i>, CallInstrValue<'i>),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, PartialEq, Eq)]
|
||||
pub struct Call<'i> {
|
||||
pub peer_part: PeerPart<'i>,
|
||||
pub function_part: FunctionPart<'i>,
|
||||
pub args: Rc<Vec<CallArgValue<'i>>>,
|
||||
pub args: Rc<Vec<CallInstrArgValue<'i>>>,
|
||||
pub output: CallOutputValue<'i>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||
pub enum CallArgValue<'i> {
|
||||
pub enum CallInstrValue<'i> {
|
||||
InitPeerId,
|
||||
Literal(&'i str),
|
||||
Variable(&'i str),
|
||||
JsonPath { variable: &'i str, path: &'i str },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||
pub enum CallInstrArgValue<'i> {
|
||||
InitPeerId,
|
||||
LastError,
|
||||
Literal(&'i str),
|
||||
Variable(&'i str),
|
||||
JsonPath { variable: &'i str, path: &'i str },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash, Serialize, Deserialize)]
|
||||
pub enum IterableValue<'i> {
|
||||
Variable(&'i str),
|
||||
|
@ -184,6 +184,7 @@ fn string_to_token(input: &str, start_pos: usize) -> Result<Token, LexerError> {
|
||||
MISMATCH_INSTR => Ok(Token::MisMatch),
|
||||
|
||||
INIT_PEER_ID => Ok(Token::InitPeerId),
|
||||
LAST_ERROR => Ok(Token::LastError),
|
||||
|
||||
str if str.ends_with(ACC_END_TAG) => try_parse_accumulator(str, start_pos),
|
||||
str => try_parse_call_variable(str, start_pos),
|
||||
@ -240,6 +241,7 @@ const MATCH_INSTR: &str = "match";
|
||||
const MISMATCH_INSTR: &str = "mismatch";
|
||||
|
||||
const INIT_PEER_ID: &str = "%init_peer_id%";
|
||||
const LAST_ERROR: &str = "%last_error%";
|
||||
|
||||
const ACC_END_TAG: &str = "[]";
|
||||
|
||||
|
@ -27,6 +27,7 @@ pub enum Token<'input> {
|
||||
Accumulator(&'input str),
|
||||
|
||||
InitPeerId,
|
||||
LastError,
|
||||
|
||||
Call,
|
||||
Seq,
|
||||
|
@ -27,7 +27,8 @@ fn parse(source_code: &str) -> Instruction {
|
||||
#[test]
|
||||
fn parse_seq() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -41,15 +42,18 @@ fn parse_seq() {
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Variable("peerid")),
|
||||
function_part: FuncName(Variable("function")),
|
||||
peer_part: PeerPk(CallInstrValue::Variable("peerid")),
|
||||
function_part: FuncName(CallInstrValue::Variable("function")),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("output"),
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("id")),
|
||||
function_part: FuncName(Literal("f")),
|
||||
args: Rc::new(vec![Literal("hello"), Variable("name")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("id")),
|
||||
function_part: FuncName(CallInstrValue::Literal("f")),
|
||||
args: Rc::new(vec![
|
||||
CallInstrArgValue::Literal("hello"),
|
||||
CallInstrArgValue::Variable("name"),
|
||||
]),
|
||||
output: None,
|
||||
}),
|
||||
);
|
||||
@ -59,7 +63,8 @@ fn parse_seq() {
|
||||
#[test]
|
||||
fn parse_seq_seq() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -77,22 +82,31 @@ fn parse_seq_seq() {
|
||||
let expected = seq(
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Variable("peerid")),
|
||||
function_part: FuncName(Variable("function")),
|
||||
peer_part: PeerPk(CallInstrValue::Variable("peerid")),
|
||||
function_part: FuncName(CallInstrValue::Variable("function")),
|
||||
args: Rc::new(vec![]),
|
||||
output: None,
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPkWithServiceId(Variable("peerid"), Variable("serviceA")),
|
||||
function_part: ServiceIdWithFuncName(Literal("serviceB"), Variable("function")),
|
||||
peer_part: PeerPkWithServiceId(
|
||||
CallInstrValue::Variable("peerid"),
|
||||
CallInstrValue::Variable("serviceA"),
|
||||
),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("serviceB"),
|
||||
CallInstrValue::Variable("function"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: None,
|
||||
}),
|
||||
),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("id")),
|
||||
function_part: FuncName(Literal("f")),
|
||||
args: Rc::new(vec![Literal("hello"), Variable("name")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("id")),
|
||||
function_part: FuncName(CallInstrValue::Literal("f")),
|
||||
args: Rc::new(vec![
|
||||
CallInstrArgValue::Literal("hello"),
|
||||
CallInstrArgValue::Variable("name"),
|
||||
]),
|
||||
output: Accumulator("output"),
|
||||
}),
|
||||
);
|
||||
@ -102,7 +116,8 @@ fn parse_seq_seq() {
|
||||
#[test]
|
||||
fn parse_json_path() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -112,12 +127,15 @@ fn parse_json_path() {
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = Instruction::Call(Call {
|
||||
peer_part: PeerPk(JsonPath {
|
||||
peer_part: PeerPk(CallInstrValue::JsonPath {
|
||||
variable: "id",
|
||||
path: "$.a",
|
||||
}),
|
||||
function_part: FuncName(Literal("f")),
|
||||
args: Rc::new(vec![Literal("hello"), Variable("name")]),
|
||||
function_part: FuncName(CallInstrValue::Literal("f")),
|
||||
args: Rc::new(vec![
|
||||
CallInstrArgValue::Literal("hello"),
|
||||
CallInstrArgValue::Variable("name"),
|
||||
]),
|
||||
output: Accumulator("void"),
|
||||
});
|
||||
assert_eq!(instruction, expected);
|
||||
@ -126,7 +144,7 @@ fn parse_json_path() {
|
||||
#[test]
|
||||
fn parse_json_path_complex() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -140,20 +158,20 @@ fn parse_json_path_complex() {
|
||||
let instruction = parse(source_code);
|
||||
let expected = seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(JsonPath {
|
||||
peer_part: PeerPk(CallInstrValue::JsonPath {
|
||||
variable: "m",
|
||||
path: "$.[1]",
|
||||
}),
|
||||
function_part: FuncName(Literal("f")),
|
||||
function_part: FuncName(CallInstrValue::Literal("f")),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("void"),
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(JsonPath {
|
||||
peer_part: PeerPk(CallInstrValue::JsonPath {
|
||||
variable: "m",
|
||||
path: r#"$.abc["c"].cde[a][0].cde["bcd"]"#,
|
||||
}),
|
||||
function_part: FuncName(Literal("f")),
|
||||
function_part: FuncName(CallInstrValue::Literal("f")),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("void"),
|
||||
}),
|
||||
@ -164,7 +182,8 @@ fn parse_json_path_complex() {
|
||||
#[test]
|
||||
fn json_path_square_braces() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -174,17 +193,20 @@ fn json_path_square_braces() {
|
||||
"#;
|
||||
let instruction = parse(source_code);
|
||||
let expected = Instruction::Call(Call {
|
||||
peer_part: PeerPk(JsonPath {
|
||||
peer_part: PeerPk(CallInstrValue::JsonPath {
|
||||
variable: "u",
|
||||
path: r#"$["peer_id"]"#,
|
||||
}),
|
||||
function_part: ServiceIdWithFuncName(Literal("return"), Literal("")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("return"),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![
|
||||
JsonPath {
|
||||
CallInstrArgValue::JsonPath {
|
||||
variable: "u",
|
||||
path: r#"$["peer_id"].cde[0]["abc"].abc"#,
|
||||
},
|
||||
JsonPath {
|
||||
CallInstrArgValue::JsonPath {
|
||||
variable: "u",
|
||||
path: r#"$["name"]"#,
|
||||
},
|
||||
@ -295,7 +317,7 @@ fn parse_fold_with_xor_par_seq() {
|
||||
#[test]
|
||||
fn parse_init_peer_id() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -313,17 +335,20 @@ fn parse_init_peer_id() {
|
||||
let instruction = parse(&source_code.as_ref());
|
||||
let expected = seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal(&peer_id)),
|
||||
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
Literal("local_service_id"),
|
||||
Literal("local_fn_name"),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: None,
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(InitPeerId),
|
||||
function_part: ServiceIdWithFuncName(Literal("service_id"), Literal("fn_name")),
|
||||
peer_part: PeerPk(CallInstrValue::InitPeerId),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: None,
|
||||
}),
|
||||
@ -332,10 +357,44 @@ fn parse_init_peer_id() {
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_last_error() {
|
||||
use ast::Call;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
|
||||
let source_code = format!(
|
||||
r#"
|
||||
(seq
|
||||
(call %init_peer_id% ("service_id" "fn_name") [%last_error%])
|
||||
(null)
|
||||
)"#,
|
||||
);
|
||||
|
||||
let instruction = parse(&source_code.as_ref());
|
||||
let expected = seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(CallInstrValue::InitPeerId),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::LastError]),
|
||||
output: None,
|
||||
}),
|
||||
Instruction::Null(ast::Null),
|
||||
);
|
||||
|
||||
assert_eq!(instruction, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn seq_par_call() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -357,26 +416,29 @@ fn seq_par_call() {
|
||||
let expected = seq(
|
||||
par(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal(&peer_id)),
|
||||
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
Literal("local_service_id"),
|
||||
Literal("local_fn_name"),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("result_1"),
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal(&peer_id)),
|
||||
function_part: ServiceIdWithFuncName(Literal("service_id"), Literal("fn_name")),
|
||||
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("service_id"),
|
||||
CallInstrValue::Literal("fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("g"),
|
||||
}),
|
||||
),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal(&peer_id)),
|
||||
peer_part: PeerPk(CallInstrValue::Literal(&peer_id)),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
Literal("local_service_id"),
|
||||
Literal("local_fn_name"),
|
||||
CallInstrValue::Literal("local_service_id"),
|
||||
CallInstrValue::Literal("local_fn_name"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: Scalar("result_2"),
|
||||
@ -389,7 +451,8 @@ fn seq_par_call() {
|
||||
#[test]
|
||||
fn seq_with_empty_and_dash() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrArgValue;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -420,50 +483,74 @@ fn seq_with_empty_and_dash() {
|
||||
seq(
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(Literal(""), Literal("")),
|
||||
args: Rc::new(vec![Literal("module-bytes")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Literal("module-bytes")]),
|
||||
output: Scalar("module-bytes"),
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(Literal(""), Literal("")),
|
||||
args: Rc::new(vec![Literal("module_config")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Literal("module_config")]),
|
||||
output: Scalar("module_config"),
|
||||
}),
|
||||
),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(Literal(""), Literal("")),
|
||||
args: Rc::new(vec![Literal("blueprint")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("set_variables")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Literal("blueprint")]),
|
||||
output: Scalar("blueprint"),
|
||||
}),
|
||||
),
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(Literal("add_module"), Literal("")),
|
||||
args: Rc::new(vec![Variable("module-bytes"), Variable("module_config")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("add_module"),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![
|
||||
CallInstrArgValue::Variable("module-bytes"),
|
||||
CallInstrArgValue::Variable("module_config"),
|
||||
]),
|
||||
output: Scalar("module"),
|
||||
}),
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(Literal("add_blueprint"), Literal("")),
|
||||
args: Rc::new(vec![Variable("blueprint")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("add_blueprint"),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Variable("blueprint")]),
|
||||
output: Scalar("blueprint_id"),
|
||||
}),
|
||||
seq(
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(Literal("create"), Literal("")),
|
||||
args: Rc::new(vec![Variable("blueprint_id")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("A")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal("create"),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Variable("blueprint_id")]),
|
||||
output: Scalar("service_id"),
|
||||
}),
|
||||
Instruction::Call(Call {
|
||||
peer_part: PeerPk(Literal("remote_peer_id")),
|
||||
function_part: ServiceIdWithFuncName(Literal(""), Literal("")),
|
||||
args: Rc::new(vec![Variable("service_id")]),
|
||||
peer_part: PeerPk(CallInstrValue::Literal("remote_peer_id")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Literal(""),
|
||||
CallInstrValue::Literal(""),
|
||||
),
|
||||
args: Rc::new(vec![CallInstrArgValue::Variable("service_id")]),
|
||||
output: Scalar("client_result"),
|
||||
}),
|
||||
),
|
||||
@ -477,7 +564,7 @@ fn seq_with_empty_and_dash() {
|
||||
#[test]
|
||||
fn no_output() {
|
||||
use ast::Call;
|
||||
use ast::CallArgValue::*;
|
||||
use ast::CallInstrValue;
|
||||
use ast::CallOutputValue::*;
|
||||
use ast::FunctionPart::*;
|
||||
use ast::PeerPart::*;
|
||||
@ -487,8 +574,11 @@ fn no_output() {
|
||||
"#;
|
||||
let instruction = parse(&source_code.as_ref());
|
||||
let expected = Instruction::Call(Call {
|
||||
peer_part: PeerPk(Variable("peer")),
|
||||
function_part: ServiceIdWithFuncName(Variable("service"), Variable("fname")),
|
||||
peer_part: PeerPk(CallInstrValue::Variable("peer")),
|
||||
function_part: ServiceIdWithFuncName(
|
||||
CallInstrValue::Variable("service"),
|
||||
CallInstrValue::Variable("fname"),
|
||||
),
|
||||
args: Rc::new(vec![]),
|
||||
output: None,
|
||||
});
|
||||
|
@ -49,4 +49,11 @@ impl SecurityTetraplet {
|
||||
json_path: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_triplet(triplet: Rc<ResolvedTriplet>) -> Self {
|
||||
Self {
|
||||
triplet,
|
||||
json_path: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,13 +77,10 @@ pub fn echo_string_call_service() -> CallServiceClosure {
|
||||
};
|
||||
|
||||
let arg: Vec<String> = serde_json::from_str(arg).unwrap();
|
||||
let arg = serde_json::to_string(&arg[0]).unwrap();
|
||||
|
||||
Some(IValue::Record(
|
||||
NEVec::new(vec![
|
||||
IValue::S32(0),
|
||||
IValue::String(format!("\"{}\"", arg[0])),
|
||||
])
|
||||
.unwrap(),
|
||||
NEVec::new(vec![IValue::S32(0), IValue::String(arg)]).unwrap(),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -21,7 +21,9 @@ use crate::StepperOutcome;
|
||||
use crate::STEPPER_SUCCESS;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
use std::hash::Hash;
|
||||
use std::rc::Rc;
|
||||
|
||||
const EXECUTION_ERRORS_START_ID: i32 = 1000;
|
||||
|
||||
@ -58,7 +60,7 @@ pub(crate) fn from_preparation_error(data: impl Into<Vec<u8>>, err: PreparationE
|
||||
|
||||
/// Create StepperOutcome from supplied data, next_peer_pks and error,
|
||||
/// set ret_code based on the error.
|
||||
pub(crate) fn from_execution_error<T>(data: &T, next_peer_pks: Vec<String>, err: ExecutionError) -> StepperOutcome
|
||||
pub(crate) fn from_execution_error<T>(data: &T, next_peer_pks: Vec<String>, err: Rc<ExecutionError>) -> StepperOutcome
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
|
@ -31,6 +31,12 @@ pub struct CallServiceResult {
|
||||
pub result: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CallServiceResult {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ret_code: {}, result: '{}'", self.ret_code, self.result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fce")]
|
||||
pub(crate) use fce_target::call_service;
|
||||
#[cfg(feature = "fce")]
|
||||
|
@ -19,10 +19,19 @@ mod avalue;
|
||||
pub(crate) use avalue::AValue;
|
||||
pub(crate) use avalue::ResolvedCallResult;
|
||||
|
||||
use crate::execution::ExecutionError;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Contains all necessary state needed to execute aqua script.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ExecutionCtx<'i> {
|
||||
/// Contains all set variables.
|
||||
// TODO: use shared string (Rc<String>) to avoid copying.
|
||||
@ -37,6 +46,10 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
/// PeerId of a peer send this aqua script.
|
||||
pub init_peer_id: String,
|
||||
|
||||
/// Last error produced by local service.
|
||||
/// None means that there weren't any error.
|
||||
pub last_error: Option<LastErrorDescriptor>,
|
||||
|
||||
/// Indicates that previous executed subtree is complete.
|
||||
/// A subtree treats as a complete if all subtree elements satisfy the following rules:
|
||||
/// - at least one of par subtrees is completed
|
||||
@ -49,15 +62,46 @@ pub(crate) struct ExecutionCtx<'i> {
|
||||
pub met_folds: VecDeque<&'i str>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct LastErrorDescriptor {
|
||||
pub(crate) error: Rc<ExecutionError>,
|
||||
pub(crate) instruction: String,
|
||||
pub(crate) tetraplet: Option<SecurityTetraplet>,
|
||||
}
|
||||
|
||||
impl LastErrorDescriptor {
|
||||
pub(crate) fn new(error: Rc<ExecutionError>, instruction: String, tetraplet: Option<SecurityTetraplet>) -> Self {
|
||||
Self {
|
||||
error,
|
||||
instruction,
|
||||
tetraplet,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> String {
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub(crate) struct LastError<'s> {
|
||||
pub(crate) error: String,
|
||||
pub(crate) instruction: &'s str,
|
||||
}
|
||||
|
||||
let error = format!("{}", &self.error);
|
||||
let error_with_location = LastError {
|
||||
error,
|
||||
instruction: &self.instruction,
|
||||
};
|
||||
|
||||
serde_json::to_string(&error_with_location).expect("default serializer shouldn't fail")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i> ExecutionCtx<'i> {
|
||||
pub(crate) fn new(current_peer_id: String, init_peer_id: String) -> Self {
|
||||
Self {
|
||||
data_cache: HashMap::new(),
|
||||
next_peer_pks: vec![],
|
||||
current_peer_id,
|
||||
init_peer_id,
|
||||
subtree_complete: true,
|
||||
met_folds: VecDeque::new(),
|
||||
..<_>::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ pub enum CallResult {
|
||||
Executed(Rc<JValue>),
|
||||
|
||||
/// call_service ended with a service error.
|
||||
CallServiceFailed(String),
|
||||
CallServiceFailed(i32, Rc<String>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@ -49,7 +49,7 @@ impl std::fmt::Display for ExecutedState {
|
||||
Par(left, right) => write!(f, "Par({}, {})", left, right),
|
||||
Call(RequestSentBy(peer_id)) => write!(f, "RequestSentBy({})", peer_id),
|
||||
Call(Executed(result)) => write!(f, "Executed({:?})", result),
|
||||
Call(CallServiceFailed(err_msg)) => write!(f, "CallServiceFailed({})", err_msg),
|
||||
Call(CallServiceFailed(ret_code, err_msg)) => write!(f, "CallServiceFailed({}, {})", ret_code, err_msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,5 +27,6 @@ pub mod execution_trace {
|
||||
pub(crate) mod execution {
|
||||
pub(crate) use super::execution_context::AValue;
|
||||
pub(crate) use super::execution_context::ExecutionCtx;
|
||||
pub(crate) use super::execution_context::LastErrorDescriptor;
|
||||
pub(crate) use super::execution_context::ResolvedCallResult;
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use super::ExecutionTraceCtx;
|
||||
use crate::contexts::execution::LastErrorDescriptor;
|
||||
use crate::log_instruction;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::Call;
|
||||
|
||||
@ -45,8 +47,23 @@ impl<'i> super::ExecutableInstruction<'i> for Call<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
log_instruction!(call, exec_ctx, trace_ctx);
|
||||
|
||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx)?;
|
||||
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx)
|
||||
let resolved_call = joinable!(ResolvedCall::new(self, exec_ctx), exec_ctx).map_err(|e| {
|
||||
let instruction = format!("{:?}", self);
|
||||
let last_error = LastErrorDescriptor::new(e.clone(), instruction, None);
|
||||
exec_ctx.last_error = Some(last_error);
|
||||
|
||||
e
|
||||
})?;
|
||||
|
||||
let triplet = resolved_call.as_triplet();
|
||||
joinable!(resolved_call.execute(exec_ctx, trace_ctx), exec_ctx).map_err(|e| {
|
||||
let tetraplet = SecurityTetraplet::from_triplet(triplet);
|
||||
let instruction = format!("{:?}", self);
|
||||
let last_error = LastErrorDescriptor::new(e.clone(), instruction, Some(tetraplet));
|
||||
exec_ctx.last_error = Some(last_error);
|
||||
|
||||
e
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ use super::Call;
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::build_targets::CallServiceResult;
|
||||
use crate::build_targets::CALL_SERVICE_SUCCESS;
|
||||
use crate::contexts::execution_trace::*;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
@ -29,7 +30,7 @@ use crate::JValue;
|
||||
use crate::ResolvedTriplet;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::{CallArgValue, CallOutputValue};
|
||||
use air_parser::ast::{CallInstrArgValue, CallOutputValue};
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -37,7 +38,7 @@ use std::rc::Rc;
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub(super) struct ResolvedCall<'i> {
|
||||
triplet: Rc<ResolvedTriplet>,
|
||||
function_arg_paths: Rc<Vec<CallArgValue<'i>>>,
|
||||
function_arg_paths: Rc<Vec<CallInstrArgValue<'i>>>,
|
||||
output: CallOutputValue<'i>,
|
||||
}
|
||||
|
||||
@ -67,7 +68,7 @@ impl<'i> ResolvedCall<'i> {
|
||||
exec_ctx: &mut ExecutionCtx<'i>,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
) -> ExecutionResult<()> {
|
||||
use CallResult::*;
|
||||
use CallResult::Executed;
|
||||
use ExecutedState::Call;
|
||||
use ExecutionError::CallServiceResultDeError as DeError;
|
||||
|
||||
@ -88,7 +89,7 @@ impl<'i> ResolvedCall<'i> {
|
||||
tetraplets,
|
||||
} = self.resolve_args(exec_ctx)?;
|
||||
|
||||
let tetraplets = serde_json::to_string(&tetraplets).expect("default serializer shouldn't fail");
|
||||
let serialized_tetraplets = serde_json::to_string(&tetraplets).expect("default serializer shouldn't fail");
|
||||
|
||||
let service_result = unsafe {
|
||||
crate::build_targets::call_service(
|
||||
@ -96,17 +97,12 @@ impl<'i> ResolvedCall<'i> {
|
||||
self.triplet.service_id.clone(),
|
||||
self.triplet.function_name.clone(),
|
||||
call_arguments,
|
||||
tetraplets,
|
||||
serialized_tetraplets,
|
||||
)
|
||||
};
|
||||
|
||||
// check that service call succeeded
|
||||
if service_result.ret_code != CALL_SERVICE_SUCCESS {
|
||||
trace_ctx
|
||||
.new_trace
|
||||
.push_back(Call(CallServiceFailed(service_result.result.clone())));
|
||||
return Err(ExecutionError::LocalServiceError(service_result.result));
|
||||
}
|
||||
let service_result = handle_service_error(service_result, trace_ctx)?;
|
||||
|
||||
let result: JValue = serde_json::from_str(&service_result.result).map_err(|e| DeError(service_result, e))?;
|
||||
let result = Rc::new(result);
|
||||
@ -125,6 +121,10 @@ impl<'i> ResolvedCall<'i> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn as_triplet(&self) -> Rc<ResolvedTriplet> {
|
||||
self.triplet.clone()
|
||||
}
|
||||
|
||||
/// Determine whether this call should be really called and adjust prev executed trace accordingly.
|
||||
fn prepare_executed_state(
|
||||
&self,
|
||||
@ -177,3 +177,25 @@ impl<'i> ResolvedCall<'i> {
|
||||
Ok(resolved_arguments)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_service_error<'i>(
|
||||
service_result: CallServiceResult,
|
||||
trace_ctx: &mut ExecutionTraceCtx,
|
||||
) -> ExecutionResult<CallServiceResult> {
|
||||
use CallResult::CallServiceFailed;
|
||||
use ExecutedState::Call;
|
||||
|
||||
if service_result.ret_code == CALL_SERVICE_SUCCESS {
|
||||
return Ok(service_result);
|
||||
}
|
||||
|
||||
let error_message = Rc::new(service_result.result);
|
||||
let error = ExecutionError::LocalServiceError(service_result.ret_code, error_message.clone());
|
||||
let error = Rc::new(error);
|
||||
|
||||
trace_ctx
|
||||
.new_trace
|
||||
.push_back(Call(CallServiceFailed(service_result.ret_code, error_message)));
|
||||
|
||||
Err(error)
|
||||
}
|
||||
|
@ -17,17 +17,18 @@
|
||||
use super::ExecutionCtx;
|
||||
use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::exec_err;
|
||||
use crate::JValue;
|
||||
|
||||
use air_parser::ast::{CallArgValue, FunctionPart, PeerPart};
|
||||
use air_parser::ast::{CallInstrValue, FunctionPart, PeerPart};
|
||||
use polyplets::ResolvedTriplet;
|
||||
|
||||
/// Triplet represents a location of the executable code in the network.
|
||||
/// It is build from `PeerPart` and `FunctionPart` of a `Call` instruction.
|
||||
pub(super) struct Triplet<'a, 'i> {
|
||||
pub(super) peer_pk: &'a CallArgValue<'i>,
|
||||
pub(super) service_id: &'a CallArgValue<'i>,
|
||||
pub(super) function_name: &'a CallArgValue<'i>,
|
||||
pub(super) peer_pk: &'a CallInstrValue<'i>,
|
||||
pub(super) service_id: &'a CallInstrValue<'i>,
|
||||
pub(super) function_name: &'a CallInstrValue<'i>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Triplet<'a, 'i> {
|
||||
@ -44,7 +45,7 @@ impl<'a, 'i> Triplet<'a, 'i> {
|
||||
Ok((peer_pk, peer_service_id, func_name))
|
||||
}
|
||||
(PeerPk(peer_pk), ServiceIdWithFuncName(service_id, func_name)) => Ok((peer_pk, service_id, func_name)),
|
||||
(PeerPk(_), FuncName(_)) => Err(ExecutionError::InstructionError(String::from(
|
||||
(PeerPk(_), FuncName(_)) => exec_err!(ExecutionError::InstructionError(String::from(
|
||||
"call should have service id specified by peer part or function part",
|
||||
))),
|
||||
}?;
|
||||
@ -77,18 +78,18 @@ impl<'a, 'i> Triplet<'a, 'i> {
|
||||
|
||||
/// Resolve value to string by either resolving variable from `ExecutionCtx`, taking literal value, or etc.
|
||||
// TODO: return Rc<String> to avoid excess cloning
|
||||
fn resolve_to_string<'i>(value: &CallArgValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
||||
fn resolve_to_string<'i>(value: &CallInstrValue<'i>, ctx: &ExecutionCtx<'i>) -> ExecutionResult<String> {
|
||||
use crate::execution::utils::resolve_to_jvaluable;
|
||||
|
||||
let resolved = match value {
|
||||
CallArgValue::InitPeerId => ctx.init_peer_id.clone(),
|
||||
CallArgValue::Literal(value) => value.to_string(),
|
||||
CallArgValue::Variable(name) => {
|
||||
CallInstrValue::InitPeerId => ctx.init_peer_id.clone(),
|
||||
CallInstrValue::Literal(value) => value.to_string(),
|
||||
CallInstrValue::Variable(name) => {
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let jvalue = resolved.into_jvalue();
|
||||
jvalue_to_string(jvalue)?
|
||||
}
|
||||
CallArgValue::JsonPath { variable, path } => {
|
||||
CallInstrValue::JsonPath { variable, path } => {
|
||||
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
||||
let resolved = resolved.apply_json_path(path)?;
|
||||
vec_to_string(resolved, path)?
|
||||
@ -103,17 +104,17 @@ fn jvalue_to_string(jvalue: JValue) -> ExecutionResult<String> {
|
||||
|
||||
match jvalue {
|
||||
JValue::String(s) => Ok(s),
|
||||
_ => Err(IncompatibleJValueType(jvalue, "string")),
|
||||
_ => exec_err!(IncompatibleJValueType(jvalue, "string")),
|
||||
}
|
||||
}
|
||||
|
||||
fn vec_to_string(values: Vec<&JValue>, json_path: &str) -> ExecutionResult<String> {
|
||||
if values.is_empty() {
|
||||
return Err(ExecutionError::VariableNotFound(json_path.to_string()));
|
||||
return exec_err!(ExecutionError::VariableNotFound(json_path.to_string()));
|
||||
}
|
||||
|
||||
if values.len() != 1 {
|
||||
return Err(ExecutionError::MultipleValuesInJsonPath(json_path.to_string()));
|
||||
return exec_err!(ExecutionError::MultipleValuesInJsonPath(json_path.to_string()));
|
||||
}
|
||||
|
||||
jvalue_to_string(values[0].clone())
|
||||
|
@ -19,6 +19,7 @@ use super::ExecutionError;
|
||||
use super::ExecutionResult;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use crate::contexts::execution_trace::*;
|
||||
use crate::exec_err;
|
||||
use crate::log_targets::EXECUTED_STATE_CHANGING;
|
||||
use crate::JValue;
|
||||
|
||||
@ -60,13 +61,13 @@ pub(super) fn set_local_call_result<'i>(
|
||||
// check that current execution flow is inside a fold block
|
||||
if exec_ctx.met_folds.is_empty() {
|
||||
// shadowing is allowed only inside fold blocks
|
||||
return Err(MultipleVariablesFound(entry.key().clone()));
|
||||
return exec_err!(MultipleVariablesFound(entry.key().clone()));
|
||||
}
|
||||
|
||||
match entry.get() {
|
||||
AValue::JValueRef(_) => {}
|
||||
// shadowing is allowed only for scalar values
|
||||
_ => return Err(ShadowingError(entry.key().clone())),
|
||||
_ => return exec_err!(ShadowingError(entry.key().clone())),
|
||||
};
|
||||
|
||||
entry.insert(AValue::JValueRef(executed_result));
|
||||
@ -78,7 +79,7 @@ pub(super) fn set_local_call_result<'i>(
|
||||
Occupied(mut entry) => match entry.get_mut() {
|
||||
// if result is an array, insert result to the end of the array
|
||||
AValue::JValueAccumulatorRef(values) => values.borrow_mut().push(executed_result),
|
||||
v => return Err(IncompatibleAValueType(format!("{}", v), String::from("Array"))),
|
||||
v => return exec_err!(IncompatibleAValueType(format!("{}", v), String::from("Array"))),
|
||||
},
|
||||
Vacant(entry) => {
|
||||
entry.insert(AValue::JValueAccumulatorRef(RefCell::new(vec![executed_result])));
|
||||
@ -124,11 +125,12 @@ pub(super) fn handle_prev_state<'i>(
|
||||
match &prev_state {
|
||||
// this call was failed on one of the previous executions,
|
||||
// here it's needed to bubble this special error up
|
||||
Call(CallServiceFailed(err_msg)) => {
|
||||
Call(CallServiceFailed(ret_code, err_msg)) => {
|
||||
let ret_code = *ret_code;
|
||||
let err_msg = err_msg.clone();
|
||||
trace_ctx.new_trace.push_back(prev_state);
|
||||
exec_ctx.subtree_complete = false;
|
||||
Err(ExecutionError::LocalServiceError(err_msg))
|
||||
exec_err!(ExecutionError::LocalServiceError(ret_code, err_msg))
|
||||
}
|
||||
Call(RequestSentBy(..)) => {
|
||||
let peer_pk = triplet.peer_pk.as_str();
|
||||
@ -149,7 +151,7 @@ pub(super) fn handle_prev_state<'i>(
|
||||
Ok(false)
|
||||
}
|
||||
// state has inconsistent order - return a error, call shouldn't be executed
|
||||
par_state @ Par(..) => Err(ExecutionError::InvalidExecutedState(
|
||||
par_state @ Par(..) => exec_err!(ExecutionError::InvalidExecutedState(
|
||||
String::from("call"),
|
||||
par_state.clone(),
|
||||
)),
|
||||
|
@ -23,6 +23,7 @@ use super::ExecutionTraceCtx;
|
||||
use super::Instruction;
|
||||
use crate::contexts::execution::AValue;
|
||||
use crate::contexts::execution::ResolvedCallResult;
|
||||
use crate::exec_err;
|
||||
use crate::execution::boxed_value::*;
|
||||
use crate::log_instruction;
|
||||
|
||||
@ -69,7 +70,7 @@ impl<'i> super::ExecutableInstruction<'i> for Fold<'i> {
|
||||
.insert(self.iterator.to_string(), AValue::JValueFoldCursor(fold_state));
|
||||
|
||||
if previous_value.is_some() {
|
||||
return Err(MultipleFoldStates(self.iterator.to_string()));
|
||||
return exec_err!(MultipleFoldStates(self.iterator.to_string()));
|
||||
}
|
||||
exec_ctx.met_folds.push_back(self.iterator);
|
||||
|
||||
@ -99,7 +100,7 @@ impl<'i> super::ExecutableInstruction<'i> for Next<'i> {
|
||||
v => {
|
||||
// it's not possible to use unreachable here
|
||||
// because at now next syntactically could be used without fold
|
||||
return Err(IncompatibleAValueType(
|
||||
return exec_err!(IncompatibleAValueType(
|
||||
format!("{}", v),
|
||||
String::from("JValueFoldCursor"),
|
||||
));
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::exec_err;
|
||||
use crate::JValue;
|
||||
use crate::ResolvedTriplet;
|
||||
use crate::SecurityTetraplet;
|
||||
@ -65,7 +66,7 @@ fn handle_instruction_variable<'ctx>(
|
||||
let call_result = ResolvedCallResult { result, triplet };
|
||||
from_call_result(call_result)?
|
||||
}
|
||||
_ => return Err(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
_ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
};
|
||||
|
||||
Ok(iterable)
|
||||
@ -83,7 +84,7 @@ fn from_call_result(call_result: ResolvedCallResult) -> ExecutionResult<Option<I
|
||||
}
|
||||
array.len()
|
||||
}
|
||||
v => return Err(IncompatibleJValueType((*v).clone(), "array")),
|
||||
v => return exec_err!(IncompatibleJValueType((*v).clone(), "array")),
|
||||
};
|
||||
|
||||
let foldable = IterableResolvedCall::init(call_result, len);
|
||||
@ -133,7 +134,7 @@ fn handle_instruction_json_path<'ctx>(
|
||||
|
||||
from_jvalues(jvalues, triplet, json_path)
|
||||
}
|
||||
_ => return Err(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
_ => return exec_err!(ExecutionError::VariableNotFound(variable_name.to_string())),
|
||||
};
|
||||
|
||||
Ok(iterable)
|
||||
@ -145,7 +146,7 @@ fn apply_json_path<'jvalue, 'str>(
|
||||
) -> ExecutionResult<Vec<&'jvalue JValue>> {
|
||||
use ExecutionError::JValueJsonPathError;
|
||||
|
||||
select(jvalue, json_path).map_err(|e| JValueJsonPathError(jvalue.clone(), json_path.to_string(), e))
|
||||
select(jvalue, json_path).map_err(|e| Rc::new(JValueJsonPathError(jvalue.clone(), json_path.to_string(), e)))
|
||||
}
|
||||
|
||||
/// Applies json_path to provided jvalues and construct IterableValue from the result and given triplet.
|
||||
|
@ -30,7 +30,7 @@ impl<'i> super::ExecutableInstruction<'i> for Match<'i> {
|
||||
let are_values_equal = are_matchable_eq(&self.left_value, &self.right_value, exec_ctx)?;
|
||||
|
||||
if !are_values_equal {
|
||||
return Err(ExecutionError::MatchWithoutXorError);
|
||||
return crate::exec_err!(ExecutionError::MatchWithoutXorError);
|
||||
}
|
||||
|
||||
self.instruction.execute(exec_ctx, trace_ctx)
|
||||
|
@ -30,7 +30,7 @@ impl<'i> super::ExecutableInstruction<'i> for MisMatch<'i> {
|
||||
let are_values_equal = are_matchable_eq(&self.left_value, &self.right_value, exec_ctx)?;
|
||||
|
||||
if are_values_equal {
|
||||
return Err(ExecutionError::MatchWithoutXorError);
|
||||
return crate::exec_err!(ExecutionError::MismatchWithoutXorError);
|
||||
}
|
||||
|
||||
self.instruction.execute(exec_ctx, trace_ctx)
|
||||
@ -184,10 +184,10 @@ mod tests {
|
||||
let res = call_vm!(set_variable_vm, "asd", script.clone(), "", "");
|
||||
let res = call_vm!(vm, "asd", script.clone(), "", res.data);
|
||||
|
||||
assert_eq!(res.ret_code, 1015);
|
||||
assert_eq!(res.ret_code, 1016);
|
||||
|
||||
let res = call_vm!(vm, "asd", script, "", res.data);
|
||||
|
||||
assert_eq!(res.ret_code, 1015);
|
||||
assert_eq!(res.ret_code, 1016);
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,26 @@ pub(crate) use fold::FoldState;
|
||||
pub(self) use super::ExecutionError;
|
||||
pub(self) use super::ExecutionResult;
|
||||
pub(self) use crate::contexts::execution::ExecutionCtx;
|
||||
pub(self) use crate::contexts::execution::LastErrorDescriptor;
|
||||
pub(self) use crate::contexts::execution_trace::ExecutionTraceCtx;
|
||||
|
||||
use air_parser::ast::Instruction;
|
||||
|
||||
/// Executes instruction and updates last error if needed.
|
||||
macro_rules! execute {
|
||||
($self:expr, $instr:expr, $exec_ctx:ident, $trace_ctx:ident) => {
|
||||
match $instr.execute($exec_ctx, $trace_ctx) {
|
||||
Err(e) => {
|
||||
let instruction = format!("{:?}", $self);
|
||||
let last_error = LastErrorDescriptor::new(e.clone(), instruction, None);
|
||||
$exec_ctx.last_error = Some(last_error);
|
||||
Err(e)
|
||||
}
|
||||
v => v,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) trait ExecutableInstruction<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()>;
|
||||
}
|
||||
@ -40,15 +56,17 @@ pub(crate) trait ExecutableInstruction<'i> {
|
||||
impl<'i> ExecutableInstruction<'i> for Instruction<'i> {
|
||||
fn execute(&self, exec_ctx: &mut ExecutionCtx<'i>, trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<()> {
|
||||
match self {
|
||||
// call isn't wrapped by the execute macro because
|
||||
// it internally sets last_error with resolved triplet
|
||||
Instruction::Call(call) => call.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Fold(fold) => fold.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Next(next) => next.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Null(null) => null.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Par(par) => par.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Seq(seq) => seq.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Xor(xor) => xor.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Match(match_) => match_.execute(exec_ctx, trace_ctx),
|
||||
Instruction::MisMatch(mismatch) => mismatch.execute(exec_ctx, trace_ctx),
|
||||
Instruction::Fold(fold) => execute!(self, fold, exec_ctx, trace_ctx),
|
||||
Instruction::Next(next) => execute!(self, next, exec_ctx, trace_ctx),
|
||||
Instruction::Null(null) => execute!(self, null, exec_ctx, trace_ctx),
|
||||
Instruction::Par(par) => execute!(self, par, exec_ctx, trace_ctx),
|
||||
Instruction::Seq(seq) => execute!(self, seq, exec_ctx, trace_ctx),
|
||||
Instruction::Xor(xor) => execute!(self, xor, exec_ctx, trace_ctx),
|
||||
Instruction::Match(match_) => execute!(self, match_, exec_ctx, trace_ctx),
|
||||
Instruction::MisMatch(mismatch) => execute!(self, mismatch, exec_ctx, trace_ctx),
|
||||
Instruction::Error => unreachable!("should not execute if parsing succeeded. QED."),
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ fn extract_subtree_sizes(trace_ctx: &mut ExecutionTraceCtx) -> ExecutionResult<(
|
||||
// unwrap is safe here because of length's been checked
|
||||
match trace_ctx.current_trace.pop_front().unwrap() {
|
||||
ExecutedState::Par(left, right) => Ok((left, right)),
|
||||
state => Err(InvalidExecutedState(String::from("par"), state)),
|
||||
state => crate::exec_err!(InvalidExecutedState(String::from("par"), state)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,10 +113,10 @@ fn execute_subtree<'i>(
|
||||
}
|
||||
// if there is a service error, update already added Par state
|
||||
// and then bubble the error up
|
||||
err @ Err(LocalServiceError(_)) => {
|
||||
Err(err) if matches!(&*err, LocalServiceError(..)) => {
|
||||
update_par_state(trace_ctx, subtree_type, current_par_pos, before_new_path_len);
|
||||
trace_ctx.current_subtree_size = before_subtree_size - subtree_size;
|
||||
err
|
||||
Err(err)
|
||||
}
|
||||
err @ Err(_) => err,
|
||||
}
|
||||
|
@ -61,7 +61,9 @@ mod tests {
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
fn fallible_call_service(fallible_service_id: String) -> CallServiceClosure {
|
||||
fn fallible_call_service(fallible_service_id: impl Into<String>) -> CallServiceClosure {
|
||||
let fallible_service_id = fallible_service_id.into();
|
||||
|
||||
Box::new(move |_, args| -> Option<IValue> {
|
||||
let builtin_service = match &args[0] {
|
||||
IValue::String(str) => str,
|
||||
@ -71,7 +73,7 @@ mod tests {
|
||||
// return a error for service with such id
|
||||
if builtin_service == &fallible_service_id {
|
||||
Some(IValue::Record(
|
||||
NEVec::new(vec![IValue::S32(1), IValue::String(String::from(r#""error""#))]).unwrap(),
|
||||
NEVec::new(vec![IValue::S32(1), IValue::String(String::from("error"))]).unwrap(),
|
||||
))
|
||||
} else {
|
||||
// return success for services with other ids
|
||||
@ -105,7 +107,10 @@ mod tests {
|
||||
let executed_call_result = Call(Executed(Rc::new(JValue::String(String::from("res")))));
|
||||
|
||||
assert_eq!(actual_trace.len(), 2);
|
||||
assert_eq!(actual_trace[0], Call(CallServiceFailed(String::from(r#""error""#))));
|
||||
assert_eq!(
|
||||
actual_trace[0],
|
||||
Call(CallServiceFailed(1, Rc::new(String::from(r#"error"#))))
|
||||
);
|
||||
assert_eq!(actual_trace[1], executed_call_result);
|
||||
|
||||
let script = format!(
|
||||
@ -223,7 +228,7 @@ mod tests {
|
||||
Call(Executed(executed_call_result.clone())),
|
||||
Call(Executed(executed_call_result.clone())),
|
||||
Par(1, 0),
|
||||
Call(CallServiceFailed(String::from(r#""error""#))),
|
||||
Call(CallServiceFailed(1, Rc::new(String::from(r#"error"#)))),
|
||||
Call(Executed(executed_call_result.clone())),
|
||||
Call(Executed(executed_call_result.clone())),
|
||||
];
|
||||
@ -234,4 +239,35 @@ mod tests {
|
||||
let actual_trace: ExecutionTrace = serde_json::from_slice(&result.data).expect("should be valid json");
|
||||
assert_eq!(actual_trace, expected_trace);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last_error_with_xor() {
|
||||
use crate::contexts::execution_trace::CallResult::*;
|
||||
use crate::contexts::execution_trace::ExecutedState::*;
|
||||
use aqua_test_utils::echo_string_call_service;
|
||||
|
||||
let faillible_peer_id = "failible_peer_id";
|
||||
let mut faillible_vm = create_aqua_vm(fallible_call_service("service_id_1"), faillible_peer_id);
|
||||
let local_peer_id = "local_peer_id";
|
||||
let mut vm = create_aqua_vm(echo_string_call_service(), local_peer_id);
|
||||
|
||||
let script = format!(
|
||||
r#"
|
||||
(xor
|
||||
(call "{0}" ("service_id_1" "local_fn_name") [] result)
|
||||
(call "{1}" ("service_id_2" "local_fn_name") [%last_error%] result)
|
||||
)"#,
|
||||
faillible_peer_id, local_peer_id,
|
||||
);
|
||||
|
||||
let res = call_vm!(faillible_vm, "asd", script.clone(), "", "");
|
||||
let res = call_vm!(vm, "asd", script, "", res.data);
|
||||
let actual_trace: ExecutionTrace = serde_json::from_slice(&res.data).expect("should be valid json");
|
||||
|
||||
let expected_state = Call(Executed(Rc::new(JValue::String(String::from(
|
||||
"{\"error\":\"Local service error: ret_code is 1, error message is \'error\'\",\"instruction\":\"Call { peer_part: PeerPk(Literal(\\\"failible_peer_id\\\")), function_part: ServiceIdWithFuncName(Literal(\\\"service_id_1\\\"), Literal(\\\"local_fn_name\\\")), args: [], output: Scalar(\\\"result\\\") }\"}"
|
||||
)))));
|
||||
|
||||
assert_eq!(actual_trace[1], expected_state);
|
||||
}
|
||||
}
|
||||
|
@ -23,11 +23,13 @@ use jsonpath_lib::JsonPathError;
|
||||
use serde_json::Error as SerdeJsonError;
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Errors arised while executing AIR script.
|
||||
#[derive(ThisError, Debug)]
|
||||
pub(crate) enum ExecutionError {
|
||||
/// Errors occurred while parsing returned by call_service value.
|
||||
#[error("call_service result '{0:?}' can't be serialized or deserialized with an error: {1:?}")]
|
||||
#[error("call_service result '{0}' can't be serialized or deserialized with an error: {1}")]
|
||||
CallServiceResultDeError(CallServiceResult, SerdeJsonError),
|
||||
|
||||
/// Semantic errors in instructions.
|
||||
@ -35,8 +37,8 @@ pub(crate) enum ExecutionError {
|
||||
InstructionError(String),
|
||||
|
||||
/// An error is occurred while calling local service via call_service.
|
||||
#[error("{0}")]
|
||||
LocalServiceError(String),
|
||||
#[error("Local service error: ret_code is {0}, error message is '{1}'")]
|
||||
LocalServiceError(i32, Rc<String>),
|
||||
|
||||
/// Value for such name isn't presence in data.
|
||||
#[error("variable with name '{0}' isn't present in data")]
|
||||
@ -47,19 +49,19 @@ pub(crate) enum ExecutionError {
|
||||
MultipleVariablesFound(String),
|
||||
|
||||
/// An error occurred while trying to apply json path to this JValue.
|
||||
#[error("variable with path '{1}' not found in '{0:?}' with an error: '{2:?}'")]
|
||||
#[error("variable with path '{1}' not found in '{0}' with an error: '{2}'")]
|
||||
JValueJsonPathError(JValue, String, JsonPathError),
|
||||
|
||||
/// An error occurred while trying to apply json path to this accumulator with JValue's.
|
||||
#[error("variable with path '{1}' not found in '{0:?}' with error: '{2:?}'")]
|
||||
#[error("variable with path '{1}' not found in '{0:?}' with error: '{2}'")]
|
||||
JValueAccJsonPathError(Vec<ResolvedCallResult>, String, JsonPathError),
|
||||
|
||||
/// Provided JValue has incompatible with target type.
|
||||
#[error("expected JValue type '{1}', but got '{0:?}' JValue")]
|
||||
#[error("expected JValue type '{1}', but got '{0}' JValue")]
|
||||
IncompatibleJValueType(JValue, &'static str),
|
||||
|
||||
/// Provided AValue has incompatible with target type.
|
||||
#[error("expected AValue type '{1}', but got '{0:?}' AValue")]
|
||||
#[error("expected AValue type '{1}', but got '{0}' AValue")]
|
||||
IncompatibleAValueType(String, String),
|
||||
|
||||
/// Multiple values found for such json path.
|
||||
@ -85,6 +87,10 @@ pub(crate) enum ExecutionError {
|
||||
/// This error type is produced by a match to notify xor that compared values aren't equal.
|
||||
#[error("match is used without corresponding xor")]
|
||||
MatchWithoutXorError,
|
||||
|
||||
/// This error type is produced by a mismatch to notify xor that compared values aren't equal.
|
||||
#[error("mismatch is used without corresponding xor")]
|
||||
MismatchWithoutXorError,
|
||||
}
|
||||
|
||||
impl ExecutionError {
|
||||
@ -94,7 +100,7 @@ impl ExecutionError {
|
||||
match self {
|
||||
CallServiceResultDeError(..) => 1,
|
||||
InstructionError(_) => 2,
|
||||
LocalServiceError(_) => 3,
|
||||
LocalServiceError(..) => 3,
|
||||
VariableNotFound(_) => 4,
|
||||
MultipleVariablesFound(_) => 5,
|
||||
JValueJsonPathError(..) => 6,
|
||||
@ -107,6 +113,7 @@ impl ExecutionError {
|
||||
InvalidExecutedState(..) => 13,
|
||||
ShadowingError(_) => 14,
|
||||
MatchWithoutXorError => 15,
|
||||
MismatchWithoutXorError => 16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,4 +23,6 @@ pub(super) use air::ExecutableInstruction;
|
||||
pub(super) use air::FoldState;
|
||||
pub(super) use errors::ExecutionError;
|
||||
|
||||
pub(self) type ExecutionResult<T> = std::result::Result<T, ExecutionError>;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(self) type ExecutionResult<T> = std::result::Result<T, Rc<ExecutionError>>;
|
||||
|
@ -22,41 +22,73 @@ use crate::execution::ExecutionResult;
|
||||
use crate::JValue;
|
||||
use crate::SecurityTetraplet;
|
||||
|
||||
use air_parser::ast::CallArgValue;
|
||||
use air_parser::ast::CallInstrArgValue;
|
||||
|
||||
/// Resolve value to called function arguments.
|
||||
pub(crate) fn resolve_to_args<'i>(
|
||||
value: &CallArgValue<'i>,
|
||||
value: &CallInstrArgValue<'i>,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
fn handle_string_arg<'i>(arg: &str, ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let jvalue = JValue::String(arg.to_string());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone());
|
||||
|
||||
Ok((jvalue, vec![tetraplet]))
|
||||
}
|
||||
|
||||
match value {
|
||||
CallArgValue::InitPeerId => handle_string_arg(ctx.init_peer_id.as_str(), ctx),
|
||||
CallArgValue::Literal(value) => handle_string_arg(value, ctx),
|
||||
CallArgValue::Variable(name) => {
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let tetraplets = resolved.as_tetraplets();
|
||||
let jvalue = resolved.into_jvalue();
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
CallArgValue::JsonPath { variable, path } => {
|
||||
let resolved = resolve_to_jvaluable(variable, ctx)?;
|
||||
let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(path)?;
|
||||
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
||||
let jvalue = JValue::Array(jvalue);
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
CallInstrArgValue::InitPeerId => prepare_string_arg(ctx.init_peer_id.as_str(), ctx),
|
||||
CallInstrArgValue::LastError => prepare_last_error(ctx),
|
||||
CallInstrArgValue::Literal(value) => prepare_string_arg(value, ctx),
|
||||
CallInstrArgValue::Variable(name) => prepare_variable(name, ctx),
|
||||
CallInstrArgValue::JsonPath { variable, path } => prepare_json_path(variable, path, ctx),
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare_string_arg<'i>(arg: &str, ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let jvalue = JValue::String(arg.to_string());
|
||||
let tetraplet = SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone());
|
||||
|
||||
Ok((jvalue, vec![tetraplet]))
|
||||
}
|
||||
|
||||
fn prepare_last_error<'i>(ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let result = match &ctx.last_error {
|
||||
Some(error) => {
|
||||
let serialized_error = error.serialize();
|
||||
let jvalue = JValue::String(serialized_error);
|
||||
let tetraplets = error
|
||||
.tetraplet
|
||||
.clone()
|
||||
.unwrap_or_else(|| SecurityTetraplet::literal_tetraplet(ctx.init_peer_id.clone()));
|
||||
|
||||
(jvalue, vec![tetraplets])
|
||||
}
|
||||
None => {
|
||||
let jvalue = JValue::String(String::new());
|
||||
let tetraplets = vec![];
|
||||
|
||||
(jvalue, tetraplets)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn prepare_variable<'i>(name: &str, ctx: &ExecutionCtx<'i>) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let tetraplets = resolved.as_tetraplets();
|
||||
let jvalue = resolved.into_jvalue();
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
fn prepare_json_path<'i>(
|
||||
name: &str,
|
||||
json_path: &str,
|
||||
ctx: &ExecutionCtx<'i>,
|
||||
) -> ExecutionResult<(JValue, Vec<SecurityTetraplet>)> {
|
||||
let resolved = resolve_to_jvaluable(name, ctx)?;
|
||||
let (jvalue, tetraplets) = resolved.apply_json_path_with_tetraplets(json_path)?;
|
||||
let jvalue = jvalue.into_iter().cloned().collect::<Vec<_>>();
|
||||
let jvalue = JValue::Array(jvalue);
|
||||
|
||||
Ok((jvalue, tetraplets))
|
||||
}
|
||||
|
||||
/// Constructs jvaluable result from `ExecutionCtx::data_cache` by name.
|
||||
pub(crate) fn resolve_to_jvaluable<'name, 'i, 'ctx>(
|
||||
name: &'name str,
|
||||
|
@ -57,3 +57,10 @@ pub mod parser {
|
||||
}
|
||||
|
||||
pub(crate) type JValue = serde_json::Value;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! exec_err {
|
||||
($err:expr) => {
|
||||
Err(std::rc::Rc::new($err))
|
||||
};
|
||||
}
|
||||
|
@ -128,14 +128,14 @@ fn merge_call(prev_call_result: CallResult, current_call_result: CallResult) ->
|
||||
use super::DataMergingError::IncompatibleCallResults;
|
||||
|
||||
match (&prev_call_result, ¤t_call_result) {
|
||||
(CallServiceFailed(prev_err_msg), CallServiceFailed(err_msg)) => {
|
||||
if prev_err_msg != err_msg {
|
||||
(CallServiceFailed(prev_ret_code, prev_err_msg), CallServiceFailed(ret_code, err_msg)) => {
|
||||
if prev_ret_code != ret_code || prev_err_msg != err_msg {
|
||||
return Err(IncompatibleCallResults(prev_call_result, current_call_result));
|
||||
}
|
||||
Ok(current_call_result)
|
||||
}
|
||||
(RequestSentBy(_), CallServiceFailed(_)) => Ok(current_call_result),
|
||||
(CallServiceFailed(_), RequestSentBy(_)) => Ok(prev_call_result),
|
||||
(RequestSentBy(_), CallServiceFailed(..)) => Ok(current_call_result),
|
||||
(CallServiceFailed(..), RequestSentBy(_)) => Ok(prev_call_result),
|
||||
(RequestSentBy(prev_sender), RequestSentBy(sender)) => {
|
||||
if prev_sender != sender {
|
||||
return Err(IncompatibleCallResults(prev_call_result, current_call_result));
|
||||
@ -152,8 +152,8 @@ fn merge_call(prev_call_result: CallResult, current_call_result: CallResult) ->
|
||||
|
||||
Ok(prev_call_result)
|
||||
}
|
||||
(CallServiceFailed(_), Executed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
|
||||
(Executed(..), CallServiceFailed(_)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
|
||||
(CallServiceFailed(..), Executed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
|
||||
(Executed(..), CallServiceFailed(..)) => Err(IncompatibleCallResults(prev_call_result, current_call_result)),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user