1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
/*
* AquaVM Workflow Engine
*
* Copyright (C) 2024 Fluence DAO
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation version 3 of the
* License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use super::ErrorAffectable;
use super::Joinable;
use crate::execution_step::execution_context::errors::StreamMapError;
use crate::execution_step::execution_context::ErrorObjectError;
use crate::execution_step::lambda_applier::LambdaError;
use crate::JValue;
use crate::ToErrorCode;
use strum::IntoEnumIterator;
use strum_macros::EnumDiscriminants;
use strum_macros::EnumIter;
use thiserror::Error as ThisError;
use std::rc::Rc;
/// Catchable errors arisen during AIR script execution. Catchable here means that these errors
/// could be handled by a xor instruction and their error_code could be used in a match
/// instruction.
#[derive(ThisError, EnumDiscriminants, Debug, Clone)]
#[strum_discriminants(derive(EnumIter))]
pub enum CatchableError {
/// An error is occurred while calling local service via call_service.
#[error("Local service error, ret_code is {0}, error message is '{1}'")]
LocalServiceError(i32, Rc<String>),
/// This error type is produced by a match to notify xor that compared values aren't equal.
#[error("compared values do not match")]
MatchValuesNotEqual,
/// This error type is produced by a mismatch to notify xor that compared values aren't equal.
#[error("compared values do not mismatch")]
MismatchValuesEqual,
/// Variable with such a name wasn't defined during AIR script execution.
/// This error type is used in order to support the join behaviour and
/// it's ok if some variable hasn't been defined yet, due to the par nature of AIR.
#[error("variable with name '{0}' wasn't defined during script execution")]
VariableNotFound(String),
/// Provided JValue has incompatible type with a requested one.
#[error(
"expected JValue type '{expected_value_type}' for the variable `{variable_name}`, but got '{actual_value}'"
)]
IncompatibleJValueType {
variable_name: String,
actual_value: JValue,
expected_value_type: &'static str,
},
/// A fold instruction must iterate over array value.
#[error("expression '{1}' returned non-array value '{0}' for fold iterable")]
FoldIteratesOverNonArray(JValue, String),
/// This error type is produced by a fail instruction.
#[error("fail with '{error}' is used without corresponding xor")]
UserError { error: JValue },
/// An error occurred while trying to apply lambda to a value.
#[error(transparent)]
LambdaApplierError(#[from] LambdaError),
/// This error type is produced by a fail instruction that tries to throw a scalar that have inappropriate type.
#[error(transparent)]
InvalidErrorObjectError(#[from] ErrorObjectError),
/// A new with this variable name was met and right after that it was accessed
/// that is prohibited.
#[error("variable with name '{0}' was cleared by new and then wasn't set")]
VariableWasNotInitializedAfterNew(String),
/// This error type is occurred when the length functor applied to a value of non-array type.
#[error("the length functor could applied only to an array-like value, but it's applied to '{0}'")]
LengthFunctorAppliedToNotArray(JValue),
/// Call gets non-string JValue resolving triplet parts.
#[error("call cannot resolve non-String triplet variable part `{variable_name}` with value '{actual_value}'")]
NonStringValueInTripletResolution {
variable_name: String,
actual_value: JValue,
},
/// Stream map related errors.
#[error(transparent)]
StreamMapError(#[from] StreamMapError),
}
impl From<LambdaError> for Rc<CatchableError> {
fn from(e: LambdaError) -> Self {
Rc::new(CatchableError::LambdaApplierError(e))
}
}
impl ToErrorCode for Rc<CatchableError> {
fn to_error_code(&self) -> i64 {
self.as_ref().to_error_code()
}
}
impl ToErrorCode for CatchableError {
fn to_error_code(&self) -> i64 {
use crate::utils::CATCHABLE_ERRORS_START_ID;
crate::generate_to_error_code!(self, CatchableError, CATCHABLE_ERRORS_START_ID)
}
}
impl ErrorAffectable for CatchableError {
fn affects_last_error(&self) -> bool {
!matches!(
self,
CatchableError::MatchValuesNotEqual | CatchableError::MismatchValuesEqual
)
}
fn affects_error(&self) -> bool {
true
}
}
macro_rules! log_join {
($($args:tt)*) => {
log::trace!(target: air_log_targets::JOIN_BEHAVIOUR, $($args)*)
}
}
#[rustfmt::skip::macros(log_join)]
impl Joinable for CatchableError {
/// Returns true, if supplied error is related to variable not found errors type.
/// Print log if this is joinable error type.
fn is_joinable(&self) -> bool {
use CatchableError::*;
match self {
VariableNotFound(var_name) => {
log_join!(" waiting for an argument with name '{}'", var_name);
true
}
_ => false,
}
}
}