mustardscript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/Cargo.lock +1579 -0
  2. package/Cargo.toml +40 -0
  3. package/LICENSE +201 -0
  4. package/README.md +828 -0
  5. package/SECURITY.md +34 -0
  6. package/crates/mustard/Cargo.toml +31 -0
  7. package/crates/mustard/src/cancellation.rs +28 -0
  8. package/crates/mustard/src/diagnostic.rs +145 -0
  9. package/crates/mustard/src/ir.rs +435 -0
  10. package/crates/mustard/src/lib.rs +21 -0
  11. package/crates/mustard/src/limits.rs +22 -0
  12. package/crates/mustard/src/parser/expressions.rs +723 -0
  13. package/crates/mustard/src/parser/mod.rs +115 -0
  14. package/crates/mustard/src/parser/operators.rs +105 -0
  15. package/crates/mustard/src/parser/patterns.rs +123 -0
  16. package/crates/mustard/src/parser/scope.rs +107 -0
  17. package/crates/mustard/src/parser/statements.rs +298 -0
  18. package/crates/mustard/src/parser/tests/acceptance.rs +339 -0
  19. package/crates/mustard/src/parser/tests/mod.rs +2 -0
  20. package/crates/mustard/src/parser/tests/rejections.rs +107 -0
  21. package/crates/mustard/src/runtime/accounting.rs +613 -0
  22. package/crates/mustard/src/runtime/api.rs +192 -0
  23. package/crates/mustard/src/runtime/async_runtime/mod.rs +5 -0
  24. package/crates/mustard/src/runtime/async_runtime/promises.rs +246 -0
  25. package/crates/mustard/src/runtime/async_runtime/reactions.rs +400 -0
  26. package/crates/mustard/src/runtime/async_runtime/scheduler.rs +224 -0
  27. package/crates/mustard/src/runtime/builtins/arrays.rs +1205 -0
  28. package/crates/mustard/src/runtime/builtins/collections.rs +573 -0
  29. package/crates/mustard/src/runtime/builtins/install.rs +501 -0
  30. package/crates/mustard/src/runtime/builtins/intl.rs +553 -0
  31. package/crates/mustard/src/runtime/builtins/mod.rs +25 -0
  32. package/crates/mustard/src/runtime/builtins/objects.rs +405 -0
  33. package/crates/mustard/src/runtime/builtins/primitives.rs +859 -0
  34. package/crates/mustard/src/runtime/builtins/promises.rs +335 -0
  35. package/crates/mustard/src/runtime/builtins/regexp.rs +356 -0
  36. package/crates/mustard/src/runtime/builtins/strings.rs +803 -0
  37. package/crates/mustard/src/runtime/builtins/support.rs +561 -0
  38. package/crates/mustard/src/runtime/bytecode.rs +123 -0
  39. package/crates/mustard/src/runtime/compiler/assignments.rs +690 -0
  40. package/crates/mustard/src/runtime/compiler/bindings.rs +92 -0
  41. package/crates/mustard/src/runtime/compiler/context.rs +46 -0
  42. package/crates/mustard/src/runtime/compiler/control.rs +342 -0
  43. package/crates/mustard/src/runtime/compiler/expressions.rs +372 -0
  44. package/crates/mustard/src/runtime/compiler/mod.rs +173 -0
  45. package/crates/mustard/src/runtime/compiler/statements.rs +459 -0
  46. package/crates/mustard/src/runtime/conversions/boundary.rs +293 -0
  47. package/crates/mustard/src/runtime/conversions/coercions.rs +217 -0
  48. package/crates/mustard/src/runtime/conversions/errors.rs +118 -0
  49. package/crates/mustard/src/runtime/conversions/mod.rs +14 -0
  50. package/crates/mustard/src/runtime/conversions/operators.rs +334 -0
  51. package/crates/mustard/src/runtime/env.rs +355 -0
  52. package/crates/mustard/src/runtime/exceptions.rs +377 -0
  53. package/crates/mustard/src/runtime/gc.rs +595 -0
  54. package/crates/mustard/src/runtime/mod.rs +318 -0
  55. package/crates/mustard/src/runtime/properties.rs +1762 -0
  56. package/crates/mustard/src/runtime/serialization.rs +127 -0
  57. package/crates/mustard/src/runtime/shared.rs +108 -0
  58. package/crates/mustard/src/runtime/snapshot_validation_tests.rs +93 -0
  59. package/crates/mustard/src/runtime/state.rs +652 -0
  60. package/crates/mustard/src/runtime/tests/async_host.rs +104 -0
  61. package/crates/mustard/src/runtime/tests/collections.rs +50 -0
  62. package/crates/mustard/src/runtime/tests/diagnostics.rs +36 -0
  63. package/crates/mustard/src/runtime/tests/exceptions.rs +122 -0
  64. package/crates/mustard/src/runtime/tests/execution.rs +553 -0
  65. package/crates/mustard/src/runtime/tests/gc.rs +533 -0
  66. package/crates/mustard/src/runtime/tests/mod.rs +56 -0
  67. package/crates/mustard/src/runtime/tests/serialization.rs +170 -0
  68. package/crates/mustard/src/runtime/validation/bytecode.rs +484 -0
  69. package/crates/mustard/src/runtime/validation/mod.rs +14 -0
  70. package/crates/mustard/src/runtime/validation/policy.rs +94 -0
  71. package/crates/mustard/src/runtime/validation/snapshot.rs +406 -0
  72. package/crates/mustard/src/runtime/validation/walk.rs +206 -0
  73. package/crates/mustard/src/runtime/vm.rs +1016 -0
  74. package/crates/mustard/src/span.rs +22 -0
  75. package/crates/mustard/src/structured.rs +107 -0
  76. package/crates/mustard-bridge/Cargo.toml +17 -0
  77. package/crates/mustard-bridge/src/codec.rs +46 -0
  78. package/crates/mustard-bridge/src/dto.rs +99 -0
  79. package/crates/mustard-bridge/src/lib.rs +12 -0
  80. package/crates/mustard-bridge/src/operations.rs +142 -0
  81. package/crates/mustard-node/Cargo.toml +24 -0
  82. package/crates/mustard-node/build.rs +3 -0
  83. package/crates/mustard-node/src/lib.rs +236 -0
  84. package/crates/mustard-sidecar/Cargo.toml +21 -0
  85. package/crates/mustard-sidecar/src/lib.rs +134 -0
  86. package/crates/mustard-sidecar/src/main.rs +36 -0
  87. package/dist/index.js +20 -0
  88. package/dist/install.js +117 -0
  89. package/dist/lib/cancellation.js +124 -0
  90. package/dist/lib/errors.js +46 -0
  91. package/dist/lib/executor.js +555 -0
  92. package/dist/lib/policy.js +292 -0
  93. package/dist/lib/progress.js +356 -0
  94. package/dist/lib/runtime.js +109 -0
  95. package/dist/lib/structured.js +286 -0
  96. package/dist/native-loader.js +227 -0
  97. package/index.d.ts +23 -0
  98. package/mustard.d.ts +220 -0
  99. package/package.json +97 -0
@@ -0,0 +1,293 @@
1
+ use super::*;
2
+
3
+ const STRUCTURED_BOUNDARY_MAX_DEPTH: usize = 128;
4
+
5
+ #[derive(Default)]
6
+ struct StructuredTraversalState {
7
+ active_arrays: HashSet<ArrayKey>,
8
+ active_objects: HashSet<ObjectKey>,
9
+ seen_arrays: HashSet<ArrayKey>,
10
+ seen_objects: HashSet<ObjectKey>,
11
+ }
12
+
13
+ impl Runtime {
14
+ pub(in crate::runtime) fn value_from_structured(
15
+ &mut self,
16
+ value: StructuredValue,
17
+ ) -> MustardResult<Value> {
18
+ self.value_from_structured_inner(value, 1)
19
+ }
20
+
21
+ fn value_from_structured_inner(
22
+ &mut self,
23
+ value: StructuredValue,
24
+ depth: usize,
25
+ ) -> MustardResult<Value> {
26
+ ensure_nested_depth(depth, structured_boundary_depth_error)?;
27
+ self.charge_native_helper_work(1)?;
28
+ Ok(match value {
29
+ StructuredValue::Undefined => Value::Undefined,
30
+ StructuredValue::Null => Value::Null,
31
+ StructuredValue::Hole => {
32
+ return Err(MustardError::runtime(
33
+ "array holes can only appear inside structured arrays",
34
+ ));
35
+ }
36
+ StructuredValue::Bool(value) => Value::Bool(value),
37
+ StructuredValue::String(value) => {
38
+ self.ensure_heap_capacity(value.len())?;
39
+ Value::String(value)
40
+ }
41
+ StructuredValue::Number(number) => Value::Number(number.to_f64()),
42
+ StructuredValue::Array(items) => {
43
+ let mut values = Vec::with_capacity(items.len());
44
+ for item in items {
45
+ values.push(match item {
46
+ StructuredValue::Hole => None,
47
+ other => Some(self.value_from_structured_inner(other, depth + 1)?),
48
+ });
49
+ }
50
+ let array = self.insert_sparse_array(values, IndexMap::new())?;
51
+ Value::Array(array)
52
+ }
53
+ StructuredValue::Object(object) => {
54
+ let mut properties = IndexMap::new();
55
+ for (key, value) in object {
56
+ properties.insert(key, self.value_from_structured_inner(value, depth + 1)?);
57
+ }
58
+ let object = self.insert_object(properties, ObjectKind::Plain)?;
59
+ Value::Object(object)
60
+ }
61
+ })
62
+ }
63
+
64
+ pub(in crate::runtime) fn value_to_structured(
65
+ &mut self,
66
+ value: Value,
67
+ ) -> MustardResult<StructuredValue> {
68
+ let mut traversal = StructuredTraversalState::default();
69
+ self.value_to_structured_inner(value, &mut traversal, 1)
70
+ }
71
+
72
+ fn value_to_structured_inner(
73
+ &mut self,
74
+ value: Value,
75
+ traversal: &mut StructuredTraversalState,
76
+ depth: usize,
77
+ ) -> MustardResult<StructuredValue> {
78
+ ensure_nested_depth(depth, structured_boundary_depth_error)?;
79
+ self.charge_native_helper_work(1)?;
80
+ Ok(match value {
81
+ Value::Undefined => StructuredValue::Undefined,
82
+ Value::Null => StructuredValue::Null,
83
+ Value::Bool(value) => StructuredValue::Bool(value),
84
+ Value::Number(value) => StructuredValue::Number(StructuredNumber::from_f64(value)),
85
+ Value::BigInt(_) => {
86
+ return Err(MustardError::runtime(
87
+ "BigInt values cannot cross the structured host boundary",
88
+ ));
89
+ }
90
+ Value::String(value) => {
91
+ self.ensure_heap_capacity(value.len())?;
92
+ StructuredValue::String(value)
93
+ }
94
+ Value::Array(array) => {
95
+ if !traversal.active_arrays.insert(array) {
96
+ return Err(structured_boundary_cycle_error());
97
+ }
98
+ if !traversal.seen_arrays.insert(array) {
99
+ traversal.active_arrays.remove(&array);
100
+ return Err(structured_boundary_shared_reference_error());
101
+ }
102
+ let elements = self
103
+ .arrays
104
+ .get(array)
105
+ .ok_or_else(|| MustardError::runtime("array missing"))?
106
+ .elements
107
+ .clone();
108
+ let result = (|| {
109
+ Ok(StructuredValue::Array(
110
+ elements
111
+ .iter()
112
+ .map(|value| match value {
113
+ Some(value) => self.value_to_structured_inner(
114
+ value.clone(),
115
+ traversal,
116
+ depth + 1,
117
+ ),
118
+ None => Ok(StructuredValue::Hole),
119
+ })
120
+ .collect::<MustardResult<Vec<_>>>()?,
121
+ ))
122
+ })();
123
+ traversal.active_arrays.remove(&array);
124
+ result?
125
+ }
126
+ Value::Object(object) => {
127
+ let object_ref = self
128
+ .objects
129
+ .get(object)
130
+ .ok_or_else(|| MustardError::runtime("object missing"))?;
131
+ match &object_ref.kind {
132
+ ObjectKind::Plain => {}
133
+ ObjectKind::Date(_) => {
134
+ return Err(MustardError::runtime(
135
+ "Date values cannot cross the structured host boundary",
136
+ ));
137
+ }
138
+ _ => {
139
+ return Err(MustardError::runtime(
140
+ "only plain objects can cross the structured host boundary",
141
+ ));
142
+ }
143
+ }
144
+ if !traversal.active_objects.insert(object) {
145
+ return Err(structured_boundary_cycle_error());
146
+ }
147
+ if !traversal.seen_objects.insert(object) {
148
+ traversal.active_objects.remove(&object);
149
+ return Err(structured_boundary_shared_reference_error());
150
+ }
151
+ let properties = object_ref.properties.clone();
152
+ let result = (|| {
153
+ Ok(StructuredValue::Object(
154
+ properties
155
+ .iter()
156
+ .map(|(key, value)| {
157
+ Ok((
158
+ key.clone(),
159
+ self.value_to_structured_inner(
160
+ value.clone(),
161
+ traversal,
162
+ depth + 1,
163
+ )?,
164
+ ))
165
+ })
166
+ .collect::<MustardResult<IndexMap<_, _>>>()?,
167
+ ))
168
+ })();
169
+ traversal.active_objects.remove(&object);
170
+ result?
171
+ }
172
+ Value::Map(_) | Value::Set(_) => {
173
+ return Err(MustardError::runtime(
174
+ "Map and Set values cannot cross the structured host boundary",
175
+ ));
176
+ }
177
+ Value::Iterator(_)
178
+ | Value::Promise(_)
179
+ | Value::Closure(_)
180
+ | Value::BuiltinFunction(_)
181
+ | Value::HostFunction(_) => {
182
+ return Err(MustardError::runtime(
183
+ "functions cannot cross the structured host boundary",
184
+ ));
185
+ }
186
+ })
187
+ }
188
+
189
+ pub(in crate::runtime) fn value_from_json(
190
+ &mut self,
191
+ value: serde_json::Value,
192
+ ) -> MustardResult<Value> {
193
+ self.value_from_json_inner(value, 1)
194
+ }
195
+
196
+ fn value_from_json_inner(
197
+ &mut self,
198
+ value: serde_json::Value,
199
+ depth: usize,
200
+ ) -> MustardResult<Value> {
201
+ ensure_nested_depth(depth, json_value_depth_error)?;
202
+ self.charge_native_helper_work(1)?;
203
+ match value {
204
+ serde_json::Value::Null => Ok(Value::Null),
205
+ serde_json::Value::Bool(value) => Ok(Value::Bool(value)),
206
+ serde_json::Value::Number(number) => Ok(Value::Number(number.as_f64().unwrap_or(0.0))),
207
+ serde_json::Value::String(value) => {
208
+ self.ensure_heap_capacity(value.len())?;
209
+ Ok(Value::String(value))
210
+ }
211
+ serde_json::Value::Array(items) => {
212
+ let mut values = Vec::with_capacity(items.len());
213
+ for item in items {
214
+ values.push(self.value_from_json_inner(item, depth + 1)?);
215
+ }
216
+ let array = self.insert_array(values, IndexMap::new())?;
217
+ Ok(Value::Array(array))
218
+ }
219
+ serde_json::Value::Object(object) => {
220
+ let mut properties = IndexMap::new();
221
+ for (key, value) in object {
222
+ properties.insert(key, self.value_from_json_inner(value, depth + 1)?);
223
+ }
224
+ let object = self.insert_object(properties, ObjectKind::Plain)?;
225
+ Ok(Value::Object(object))
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ #[allow(dead_code)]
232
+ pub(in crate::runtime) fn structured_to_json(
233
+ value: StructuredValue,
234
+ ) -> MustardResult<serde_json::Value> {
235
+ Ok(match value {
236
+ StructuredValue::Undefined => serde_json::Value::Null,
237
+ StructuredValue::Null => serde_json::Value::Null,
238
+ StructuredValue::Hole => {
239
+ return Err(MustardError::runtime(
240
+ "array holes cannot appear outside structured arrays",
241
+ ));
242
+ }
243
+ StructuredValue::Bool(value) => serde_json::Value::Bool(value),
244
+ StructuredValue::String(value) => serde_json::Value::String(value),
245
+ StructuredValue::Number(number) => match number {
246
+ StructuredNumber::Finite(value) => serde_json::Number::from_f64(value)
247
+ .map(serde_json::Value::Number)
248
+ .unwrap_or(serde_json::Value::Null),
249
+ StructuredNumber::NaN
250
+ | StructuredNumber::Infinity
251
+ | StructuredNumber::NegInfinity
252
+ | StructuredNumber::NegZero => serde_json::Value::Null,
253
+ },
254
+ StructuredValue::Array(values) => serde_json::Value::Array(
255
+ values
256
+ .into_iter()
257
+ .map(structured_to_json)
258
+ .collect::<MustardResult<Vec<_>>>()?,
259
+ ),
260
+ StructuredValue::Object(values) => serde_json::Value::Object(
261
+ values
262
+ .into_iter()
263
+ .map(|(key, value)| Ok((key, structured_to_json(value)?)))
264
+ .collect::<MustardResult<serde_json::Map<_, _>>>()?,
265
+ ),
266
+ })
267
+ }
268
+
269
+ fn structured_boundary_cycle_error() -> MustardError {
270
+ MustardError::runtime("cyclic values cannot cross the structured host boundary")
271
+ }
272
+
273
+ fn structured_boundary_shared_reference_error() -> MustardError {
274
+ MustardError::runtime("shared references cannot cross the structured host boundary")
275
+ }
276
+
277
+ fn structured_boundary_depth_error() -> MustardError {
278
+ MustardError::runtime("structured host boundary nesting limit exceeded")
279
+ }
280
+
281
+ fn json_value_depth_error() -> MustardError {
282
+ MustardError::runtime("JSON value nesting limit exceeded")
283
+ }
284
+
285
+ fn ensure_nested_depth(
286
+ depth: usize,
287
+ make_error: impl FnOnce() -> MustardError,
288
+ ) -> MustardResult<()> {
289
+ if depth > STRUCTURED_BOUNDARY_MAX_DEPTH {
290
+ return Err(make_error());
291
+ }
292
+ Ok(())
293
+ }
@@ -0,0 +1,217 @@
1
+ use super::*;
2
+
3
+ impl Runtime {
4
+ pub(in crate::runtime) fn to_number(&self, value: Value) -> MustardResult<f64> {
5
+ Ok(match value {
6
+ Value::Undefined => f64::NAN,
7
+ Value::Null => 0.0,
8
+ Value::Bool(value) => {
9
+ if value {
10
+ 1.0
11
+ } else {
12
+ 0.0
13
+ }
14
+ }
15
+ Value::Number(value) => value,
16
+ Value::BigInt(_) => {
17
+ return Err(MustardError::runtime(
18
+ "TypeError: cannot coerce BigInt values to numbers",
19
+ ));
20
+ }
21
+ Value::String(value) => value.parse::<f64>().unwrap_or(f64::NAN),
22
+ Value::Object(object) => match &self
23
+ .objects
24
+ .get(object)
25
+ .ok_or_else(|| MustardError::runtime("object missing"))?
26
+ .kind
27
+ {
28
+ ObjectKind::NumberObject(value) => *value,
29
+ ObjectKind::StringObject(value) => value.parse::<f64>().unwrap_or(f64::NAN),
30
+ ObjectKind::BooleanObject(value) => {
31
+ if *value {
32
+ 1.0
33
+ } else {
34
+ 0.0
35
+ }
36
+ }
37
+ _ => {
38
+ return Err(MustardError::runtime(
39
+ "cannot coerce complex value to number",
40
+ ));
41
+ }
42
+ },
43
+ Value::Array(_)
44
+ | Value::Map(_)
45
+ | Value::Set(_)
46
+ | Value::Iterator(_)
47
+ | Value::Promise(_)
48
+ | Value::Closure(_)
49
+ | Value::BuiltinFunction(_)
50
+ | Value::HostFunction(_) => {
51
+ return Err(MustardError::runtime(
52
+ "cannot coerce complex value to number",
53
+ ));
54
+ }
55
+ })
56
+ }
57
+
58
+ pub(in crate::runtime) fn to_integer(&self, value: Value) -> MustardResult<i64> {
59
+ let number = self.to_number(value)?;
60
+ if number.is_nan() || number == 0.0 {
61
+ Ok(0)
62
+ } else if number.is_infinite() {
63
+ Ok(if number.is_sign_positive() {
64
+ i64::MAX
65
+ } else {
66
+ i64::MIN
67
+ })
68
+ } else {
69
+ let truncated = number.trunc();
70
+ if truncated >= i64::MAX as f64 {
71
+ Ok(i64::MAX)
72
+ } else if truncated <= i64::MIN as f64 {
73
+ Ok(i64::MIN)
74
+ } else {
75
+ Ok(truncated as i64)
76
+ }
77
+ }
78
+ }
79
+
80
+ pub(in crate::runtime) fn to_string(&self, value: Value) -> MustardResult<String> {
81
+ Ok(match value {
82
+ Value::Undefined => "undefined".to_string(),
83
+ Value::Null => "null".to_string(),
84
+ Value::Bool(value) => value.to_string(),
85
+ Value::Number(value) => {
86
+ if value.fract() == 0.0 {
87
+ format!("{}", value as i64)
88
+ } else {
89
+ value.to_string()
90
+ }
91
+ }
92
+ Value::BigInt(value) => value.to_string(),
93
+ Value::String(value) => value,
94
+ Value::Array(array) => {
95
+ let array = self
96
+ .arrays
97
+ .get(array)
98
+ .ok_or_else(|| MustardError::runtime("array missing"))?;
99
+ let mut parts = Vec::new();
100
+ for value in &array.elements {
101
+ parts.push(match value {
102
+ None | Some(Value::Undefined) | Some(Value::Null) => String::new(),
103
+ Some(value) => self.to_string(value.clone())?,
104
+ });
105
+ }
106
+ parts.join(",")
107
+ }
108
+ Value::Map(_) => "[object Map]".to_string(),
109
+ Value::Set(_) => "[object Set]".to_string(),
110
+ Value::Object(object) => match &self
111
+ .objects
112
+ .get(object)
113
+ .ok_or_else(|| MustardError::runtime("object missing"))?
114
+ .kind
115
+ {
116
+ ObjectKind::Date(_) => "[object Date]".to_string(),
117
+ ObjectKind::RegExp(regex) => format!("/{}/{}", regex.pattern, regex.flags),
118
+ ObjectKind::NumberObject(value) => {
119
+ if value.fract() == 0.0 {
120
+ format!("{}", *value as i64)
121
+ } else {
122
+ value.to_string()
123
+ }
124
+ }
125
+ ObjectKind::StringObject(value) => value.clone(),
126
+ ObjectKind::BooleanObject(value) => value.to_string(),
127
+ ObjectKind::BoundFunction(_) => {
128
+ self.callable_display_string(&Value::Object(object))?
129
+ }
130
+ _ => self
131
+ .error_summary(object)?
132
+ .unwrap_or_else(|| "[object Object]".to_string()),
133
+ },
134
+ Value::Iterator(_) => "[object Iterator]".to_string(),
135
+ Value::Promise(_) => "[object Promise]".to_string(),
136
+ callable @ (Value::Closure(_) | Value::BuiltinFunction(_) | Value::HostFunction(_)) => {
137
+ self.callable_display_string(&callable)?
138
+ }
139
+ })
140
+ }
141
+
142
+ pub(in crate::runtime) fn to_property_key(&self, value: Value) -> MustardResult<String> {
143
+ match value {
144
+ Value::String(value) => Ok(value),
145
+ Value::Number(value) => Ok(format_number_key(value)),
146
+ Value::BigInt(value) => Ok(value.to_string()),
147
+ Value::Bool(value) => Ok(value.to_string()),
148
+ Value::Null => Ok("null".to_string()),
149
+ Value::Undefined => Ok("undefined".to_string()),
150
+ _ => self.to_string(value),
151
+ }
152
+ }
153
+
154
+ pub(in crate::runtime) fn to_array_items(&self, value: Value) -> MustardResult<Vec<Value>> {
155
+ match value {
156
+ Value::Array(array) => self
157
+ .arrays
158
+ .get(array)
159
+ .map(|array| {
160
+ array
161
+ .elements
162
+ .iter()
163
+ .map(|value| value.clone().unwrap_or(Value::Undefined))
164
+ .collect()
165
+ })
166
+ .ok_or_else(|| MustardError::runtime("array missing")),
167
+ Value::Undefined | Value::Null => Ok(Vec::new()),
168
+ _ => Err(MustardError::runtime(
169
+ "value is not destructurable as an array",
170
+ )),
171
+ }
172
+ }
173
+
174
+ fn callable_display_string(&self, value: &Value) -> MustardResult<String> {
175
+ Ok(match value {
176
+ Value::Closure(closure) => {
177
+ let closure = self
178
+ .closures
179
+ .get(*closure)
180
+ .ok_or_else(|| MustardError::runtime("closure missing"))?;
181
+ let function = self
182
+ .program
183
+ .functions
184
+ .get(closure.function_id)
185
+ .ok_or_else(|| MustardError::runtime("function not found"))?;
186
+ if function.display_source.is_empty() {
187
+ let name = self.callable_name(value)?;
188
+ if name.is_empty() {
189
+ "function () { [mustard code] }".to_string()
190
+ } else {
191
+ format!("function {name}() {{ [mustard code] }}")
192
+ }
193
+ } else {
194
+ function.display_source.clone()
195
+ }
196
+ }
197
+ Value::BuiltinFunction(function) => {
198
+ format!(
199
+ "function {}() {{ [native code] }}",
200
+ self.callable_name(&Value::BuiltinFunction(*function))?
201
+ )
202
+ }
203
+ Value::HostFunction(_) => "function () { [host code] }".to_string(),
204
+ Value::Object(object) => {
205
+ let object = self
206
+ .objects
207
+ .get(*object)
208
+ .ok_or_else(|| MustardError::runtime("object missing"))?;
209
+ match object.kind {
210
+ ObjectKind::BoundFunction(_) => "function () { [native code] }".to_string(),
211
+ _ => "[object Object]".to_string(),
212
+ }
213
+ }
214
+ _ => self.to_string(value.clone())?,
215
+ })
216
+ }
217
+ }
@@ -0,0 +1,118 @@
1
+ use super::*;
2
+
3
+ impl Runtime {
4
+ pub(in crate::runtime) fn make_error_object(
5
+ &mut self,
6
+ name: &str,
7
+ args: &[Value],
8
+ code: Option<String>,
9
+ details: Option<Value>,
10
+ cause: Option<Option<Value>>,
11
+ ) -> MustardResult<Value> {
12
+ let message = match args.first() {
13
+ Some(Value::Undefined) | None => String::new(),
14
+ Some(value) => self.to_string(value.clone())?,
15
+ };
16
+ let mut properties = IndexMap::from([
17
+ ("name".to_string(), Value::String(name.to_string())),
18
+ ("message".to_string(), Value::String(message)),
19
+ ]);
20
+ if let Some(code) = code {
21
+ properties.insert("code".to_string(), Value::String(code));
22
+ }
23
+ if let Some(details) = details {
24
+ properties.insert("details".to_string(), details);
25
+ }
26
+ if let Some(cause) = cause {
27
+ properties.insert("cause".to_string(), cause.unwrap_or(Value::Undefined));
28
+ }
29
+ let object = self.insert_object(properties, ObjectKind::Error(name.to_string()))?;
30
+ Ok(Value::Object(object))
31
+ }
32
+
33
+ pub(in crate::runtime) fn value_from_runtime_message(
34
+ &mut self,
35
+ message: &str,
36
+ ) -> MustardResult<Value> {
37
+ let (name, detail) = match message.split_once(": ") {
38
+ Some((name, detail)) if name == "Error" || name.ends_with("Error") => {
39
+ (name.to_string(), detail.to_string())
40
+ }
41
+ _ => ("Error".to_string(), message.to_string()),
42
+ };
43
+ self.make_error_object(&name, &[Value::String(detail)], None, None, None)
44
+ }
45
+
46
+ pub(in crate::runtime) fn value_from_host_error(
47
+ &mut self,
48
+ error: HostError,
49
+ ) -> MustardResult<Value> {
50
+ let details = match error.details {
51
+ Some(details) => Some(self.value_from_structured(details)?),
52
+ None => None,
53
+ };
54
+ self.make_error_object(
55
+ &error.name,
56
+ &[Value::String(error.message)],
57
+ error.code,
58
+ details,
59
+ None,
60
+ )
61
+ }
62
+
63
+ pub(in crate::runtime) fn render_exception(&self, value: &Value) -> MustardResult<String> {
64
+ match value {
65
+ Value::Object(object) => {
66
+ if let Some(summary) = self.error_summary(*object)? {
67
+ Ok(summary)
68
+ } else {
69
+ self.to_string(value.clone())
70
+ }
71
+ }
72
+ _ => self.to_string(value.clone()),
73
+ }
74
+ }
75
+
76
+ pub(in crate::runtime) fn error_summary(
77
+ &self,
78
+ object: ObjectKey,
79
+ ) -> MustardResult<Option<String>> {
80
+ let object = self
81
+ .objects
82
+ .get(object)
83
+ .ok_or_else(|| MustardError::runtime("object missing"))?;
84
+ let details = object.properties.get("details").cloned();
85
+ let name = object.properties.get("name").and_then(|value| match value {
86
+ Value::String(value) => Some(value.as_str()),
87
+ _ => None,
88
+ });
89
+ let message = object
90
+ .properties
91
+ .get("message")
92
+ .and_then(|value| match value {
93
+ Value::String(value) => Some(value.as_str()),
94
+ _ => None,
95
+ });
96
+
97
+ if !matches!(object.kind, ObjectKind::Error(_)) && name.is_none() && message.is_none() {
98
+ return Ok(None);
99
+ }
100
+
101
+ let mut summary = match (name, message) {
102
+ (Some(name), Some("")) => name.to_string(),
103
+ (Some(name), Some(message)) => format!("{name}: {message}"),
104
+ (Some(name), None) => name.to_string(),
105
+ (None, Some(message)) => message.to_string(),
106
+ (None, None) => "Error".to_string(),
107
+ };
108
+
109
+ if let Some(Value::String(code)) = object.properties.get("code") {
110
+ summary.push_str(&format!(" [code={code}]"));
111
+ }
112
+ if let Some(details) = details {
113
+ summary.push_str(&format!(" [details={}]", self.to_string(details)?));
114
+ }
115
+
116
+ Ok(Some(summary))
117
+ }
118
+ }
@@ -0,0 +1,14 @@
1
+ use std::collections::HashSet;
2
+
3
+ use num_bigint::BigInt;
4
+ use num_traits::{ToPrimitive, Zero};
5
+
6
+ use super::*;
7
+
8
+ mod boundary;
9
+ mod coercions;
10
+ mod errors;
11
+ mod operators;
12
+
13
+ #[allow(unused_imports)]
14
+ pub(super) use boundary::structured_to_json;