rib/interpreter/
rib_runtime_error.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::interpreter::interpreter_stack_value::RibInterpreterStackValue;
16use crate::{InstructionId, TypeHint};
17use golem_wasm_rpc::{Value, ValueAndType};
18use std::fmt::{Display, Formatter};
19
20#[derive(Debug)]
21pub enum RibRuntimeError {
22    ArithmeticError {
23        message: String,
24    },
25    CastError {
26        from: CastFrom,
27        to: TypeHint,
28    },
29    ExhaustedIterator,
30    FieldNotFound {
31        field: String,
32    },
33    FunctionInvokeError {
34        function_name: String,
35        error: Box<dyn std::error::Error + Send + Sync>,
36    },
37    IndexOutOfBound {
38        index: usize,
39        size: usize,
40    },
41    InfiniteComputation {
42        message: String,
43    },
44    InputNotFound(String),
45    InvariantViolation(InvariantViolation),
46    InvalidComparison {
47        message: String,
48        left: Option<ValueAndType>,
49        right: Option<ValueAndType>,
50    },
51    NoResult,
52    ThrownError(String),
53    TypeMismatch {
54        expected: Vec<TypeHint>,
55        found: InvalidItem,
56    },
57}
58
59impl std::error::Error for RibRuntimeError {}
60
61#[derive(Debug, Clone, PartialEq)]
62pub enum CastFrom {
63    FromValue(Value),
64    FromType(TypeHint),
65    FromCustom(String),
66}
67
68impl Display for CastFrom {
69    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
70        match self {
71            CastFrom::FromValue(value) => write!(f, "{value:?}"),
72            CastFrom::FromType(typ) => write!(f, "{typ}"),
73            CastFrom::FromCustom(custom) => write!(f, "{custom}"),
74        }
75    }
76}
77
78#[derive(Debug, Clone, PartialEq)]
79pub enum InvalidItem {
80    RuntimeValue(Value),
81    Type(TypeHint),
82    Custom(String),
83}
84
85pub fn arithmetic_error(message: &str) -> RibRuntimeError {
86    RibRuntimeError::ArithmeticError {
87        message: message.to_string(),
88    }
89}
90
91pub fn cast_error(from: Value, to: TypeHint) -> RibRuntimeError {
92    RibRuntimeError::CastError {
93        from: CastFrom::FromValue(from),
94        to,
95    }
96}
97
98pub fn cast_error_custom<T: Display>(from: T, to: TypeHint) -> RibRuntimeError {
99    RibRuntimeError::CastError {
100        from: CastFrom::FromCustom(from.to_string()),
101        to,
102    }
103}
104
105pub fn empty_stack() -> RibRuntimeError {
106    RibRuntimeError::InvariantViolation(InvariantViolation::InsufficientStackItems(1))
107}
108
109pub fn exhausted_iterator() -> RibRuntimeError {
110    RibRuntimeError::ExhaustedIterator
111}
112
113pub fn field_not_found(field_name: &str) -> RibRuntimeError {
114    RibRuntimeError::FieldNotFound {
115        field: field_name.to_string(),
116    }
117}
118
119pub fn function_invoke_fail(
120    function_name: &str,
121    error: Box<dyn std::error::Error + Send + Sync>,
122) -> RibRuntimeError {
123    RibRuntimeError::FunctionInvokeError {
124        function_name: function_name.to_string(),
125        error,
126    }
127}
128
129pub fn index_out_of_bound(index: usize, size: usize) -> RibRuntimeError {
130    RibRuntimeError::IndexOutOfBound { index, size }
131}
132
133pub fn infinite_computation(message: &str) -> RibRuntimeError {
134    RibRuntimeError::InfiniteComputation {
135        message: message.to_string(),
136    }
137}
138
139pub fn input_not_found(input_name: &str) -> RibRuntimeError {
140    RibRuntimeError::InputNotFound(input_name.to_string())
141}
142
143pub fn instruction_jump_error(instruction_id: InstructionId) -> RibRuntimeError {
144    RibRuntimeError::InvariantViolation(InvariantViolation::InstructionJumpError(instruction_id))
145}
146
147pub fn insufficient_stack_items(size: usize) -> RibRuntimeError {
148    RibRuntimeError::InvariantViolation(InvariantViolation::InsufficientStackItems(size))
149}
150
151pub fn invalid_comparison(
152    message: &str,
153    left: Option<ValueAndType>,
154    right: Option<ValueAndType>,
155) -> RibRuntimeError {
156    RibRuntimeError::InvalidComparison {
157        message: message.to_string(),
158        left,
159        right,
160    }
161}
162
163pub fn invalid_type_with_stack_value(
164    expected: Vec<TypeHint>,
165    found: RibInterpreterStackValue,
166) -> RibRuntimeError {
167    RibRuntimeError::TypeMismatch {
168        expected,
169        found: InvalidItem::Custom(found.to_string()),
170    }
171}
172
173pub fn type_mismatch_with_value(expected: Vec<TypeHint>, found: Value) -> RibRuntimeError {
174    RibRuntimeError::TypeMismatch {
175        expected,
176        found: InvalidItem::RuntimeValue(found),
177    }
178}
179
180pub fn type_mismatch_with_type_hint(expected: Vec<TypeHint>, found: TypeHint) -> RibRuntimeError {
181    RibRuntimeError::TypeMismatch {
182        expected,
183        found: InvalidItem::Type(found),
184    }
185}
186
187pub fn no_result() -> RibRuntimeError {
188    RibRuntimeError::NoResult
189}
190
191pub fn throw_error(message: &str) -> RibRuntimeError {
192    RibRuntimeError::ThrownError(message.to_string())
193}
194
195#[derive(Debug, Clone, PartialEq)]
196pub enum InvariantViolation {
197    InsufficientStackItems(usize),
198    InternalCorruptedState(String),
199    InstructionJumpError(InstructionId),
200}
201
202impl Display for RibRuntimeError {
203    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204        match self {
205            RibRuntimeError::InputNotFound(input_name) => {
206                write!(f, "input not found: {input_name}")
207            }
208            RibRuntimeError::ExhaustedIterator => write!(f, "no more values in iterator"),
209            RibRuntimeError::FieldNotFound { field } => {
210                write!(f, "field not found: {field}")
211            }
212            RibRuntimeError::InvariantViolation(violation) => {
213                write!(f, "internal error: {violation:?}")
214            }
215            RibRuntimeError::ThrownError(message) => write!(f, "error: {message}"),
216            RibRuntimeError::CastError { from, to } => {
217                write!(f, "cast error from {from} to {to}")
218            }
219            RibRuntimeError::TypeMismatch { expected, found } => {
220                write!(
221                    f,
222                    "runtime type mismatch: expected {expected:?}, found {found:?}"
223                )
224            }
225            RibRuntimeError::NoResult => write!(f, "No result"),
226            RibRuntimeError::InfiniteComputation { message } => {
227                write!(f, "infinite computation detected: {message}")
228            }
229            RibRuntimeError::IndexOutOfBound { index, size } => {
230                write!(f, "index out of bound: {index} (size: {size})")
231            }
232            RibRuntimeError::InvalidComparison {
233                message,
234                left,
235                right,
236            } => match (left, right) {
237                (Some(left), Some(right)) => {
238                    write!(
239                        f,
240                        "Invalid comparison: {message} (left: {left}, right: {right})"
241                    )
242                }
243                _ => {
244                    write!(f, "Invalid comparison: {message} ")
245                }
246            },
247            RibRuntimeError::ArithmeticError { message } => {
248                write!(f, "arithmetic error: {message}")
249            }
250            RibRuntimeError::FunctionInvokeError {
251                function_name,
252                error,
253            } => {
254                write!(f, "failed to invoke function {function_name}: {error}")
255            }
256        }
257    }
258}