Skip to main content

geam_core/runtime/value/
list.rs

1use ecow::EcoString;
2use num_bigint::BigInt;
3use thiserror::Error;
4
5use super::{BitArrayValue, CustomValue, ExternalValue, FunctionValue, Value};
6use crate::plan::{CustomType, ExternalType, FunctionType, TypeParameterId, ValueType};
7
8#[derive(Debug, Clone, PartialEq)]
9pub struct ListValue {
10    kind: ListValueKind,
11}
12
13#[derive(Debug, Error, Clone, PartialEq, Eq)]
14#[error("list value item type mismatch at index {index} (expected {expected:?}, got {actual:?})")]
15pub struct ListValueItemTypeMismatch {
16    pub index: usize,
17    pub expected: ValueType,
18    pub actual: ValueType,
19}
20
21// Empty lists still need item type metadata; variants that cannot infer it from
22// their values carry the metadata explicitly.
23#[derive(Debug, Clone, PartialEq)]
24pub(super) enum ListValueKind {
25    Parameter(TypeParameterId),
26    Int(Vec<BigInt>),
27    String(Vec<EcoString>),
28    BitArray(Vec<BitArrayValue>),
29    UtfCodepoint(Vec<char>),
30    Custom {
31        item_type: CustomType,
32        values: Vec<CustomValue>,
33    },
34    External {
35        item_type: ExternalType,
36        values: Vec<ExternalValue>,
37    },
38    Float(Vec<f64>),
39    Bool(Vec<bool>),
40    Nil(usize),
41    Tuple {
42        item_type: Vec<ValueType>,
43        values: Vec<Vec<Value>>,
44    },
45    List {
46        item_type: Box<ValueType>,
47        values: Vec<ListValue>,
48    },
49    Function {
50        item_type: FunctionType,
51        values: Vec<FunctionValue>,
52    },
53}
54
55impl ListValue {
56    pub fn int(values: Vec<BigInt>) -> Self {
57        Self {
58            kind: ListValueKind::Int(values),
59        }
60    }
61
62    pub fn string(values: Vec<EcoString>) -> Self {
63        Self {
64            kind: ListValueKind::String(values),
65        }
66    }
67
68    pub fn bit_array(values: Vec<BitArrayValue>) -> Self {
69        Self {
70            kind: ListValueKind::BitArray(values),
71        }
72    }
73
74    pub fn utf_codepoint(values: Vec<char>) -> Self {
75        Self {
76            kind: ListValueKind::UtfCodepoint(values),
77        }
78    }
79
80    pub fn try_custom(
81        item_type: CustomType,
82        values: Vec<CustomValue>,
83    ) -> Result<Self, ListValueItemTypeMismatch> {
84        let expected = ValueType::Custom(item_type.clone());
85        ensure_item_types(
86            &expected,
87            values
88                .iter()
89                .map(|value| ValueType::Custom(value.type_().clone())),
90        )?;
91        Ok(Self {
92            kind: ListValueKind::Custom { item_type, values },
93        })
94    }
95
96    pub(crate) fn from_evaluated_custom(item_type: CustomType, values: Vec<CustomValue>) -> Self {
97        Self {
98            kind: ListValueKind::Custom { item_type, values },
99        }
100    }
101
102    pub fn try_external(
103        item_type: ExternalType,
104        values: Vec<ExternalValue>,
105    ) -> Result<Self, ListValueItemTypeMismatch> {
106        let expected = ValueType::External(item_type.clone());
107        ensure_item_types(
108            &expected,
109            values
110                .iter()
111                .map(|value| ValueType::External(value.type_().clone())),
112        )?;
113        Ok(Self {
114            kind: ListValueKind::External { item_type, values },
115        })
116    }
117
118    pub(crate) fn from_evaluated_external(
119        item_type: ExternalType,
120        values: Vec<ExternalValue>,
121    ) -> Self {
122        Self {
123            kind: ListValueKind::External { item_type, values },
124        }
125    }
126
127    pub fn float(values: Vec<f64>) -> Self {
128        Self {
129            kind: ListValueKind::Float(values),
130        }
131    }
132
133    pub fn bool(values: Vec<bool>) -> Self {
134        Self {
135            kind: ListValueKind::Bool(values),
136        }
137    }
138
139    pub fn nil(len: usize) -> Self {
140        Self {
141            kind: ListValueKind::Nil(len),
142        }
143    }
144
145    pub fn try_tuple(
146        item_type: Vec<ValueType>,
147        values: Vec<Vec<Value>>,
148    ) -> Result<Self, ListValueItemTypeMismatch> {
149        let expected = ValueType::Tuple(item_type.clone());
150        ensure_item_types(
151            &expected,
152            values
153                .iter()
154                .map(|values| ValueType::Tuple(values.iter().map(Value::value_type).collect())),
155        )?;
156        Ok(Self {
157            kind: ListValueKind::Tuple { item_type, values },
158        })
159    }
160
161    pub(crate) fn from_evaluated_tuple(item_type: Vec<ValueType>, values: Vec<Vec<Value>>) -> Self {
162        Self {
163            kind: ListValueKind::Tuple { item_type, values },
164        }
165    }
166
167    pub fn try_list(
168        item_type: ValueType,
169        values: Vec<ListValue>,
170    ) -> Result<Self, ListValueItemTypeMismatch> {
171        let expected = ValueType::List(Box::new(item_type.clone()));
172        ensure_item_types(
173            &expected,
174            values
175                .iter()
176                .map(|value| ValueType::List(Box::new(value.item_type()))),
177        )?;
178        Ok(Self {
179            kind: ListValueKind::List {
180                item_type: Box::new(item_type),
181                values,
182            },
183        })
184    }
185
186    pub(crate) fn from_evaluated_list(item_type: ValueType, values: Vec<ListValue>) -> Self {
187        Self {
188            kind: ListValueKind::List {
189                item_type: Box::new(item_type),
190                values,
191            },
192        }
193    }
194
195    pub fn try_function(
196        item_type: FunctionType,
197        values: Vec<FunctionValue>,
198    ) -> Result<Self, ListValueItemTypeMismatch> {
199        let expected = ValueType::Function(Box::new(item_type.clone()));
200        ensure_item_types(
201            &expected,
202            values
203                .iter()
204                .map(|value| ValueType::Function(Box::new(value.type_()))),
205        )?;
206        Ok(Self {
207            kind: ListValueKind::Function { item_type, values },
208        })
209    }
210
211    pub(crate) fn from_evaluated_function(
212        item_type: FunctionType,
213        values: Vec<FunctionValue>,
214    ) -> Self {
215        Self {
216            kind: ListValueKind::Function { item_type, values },
217        }
218    }
219
220    pub fn empty(item_type: ValueType) -> Self {
221        match item_type {
222            ValueType::Parameter(parameter) => Self {
223                kind: ListValueKind::Parameter(parameter),
224            },
225            ValueType::Int => Self::int(Vec::new()),
226            ValueType::String => Self::string(Vec::new()),
227            ValueType::BitArray => Self::bit_array(Vec::new()),
228            ValueType::UtfCodepoint => Self::utf_codepoint(Vec::new()),
229            ValueType::Custom(item_type) => Self {
230                kind: ListValueKind::Custom {
231                    item_type,
232                    values: Vec::new(),
233                },
234            },
235            ValueType::External(item_type) => Self {
236                kind: ListValueKind::External {
237                    item_type,
238                    values: Vec::new(),
239                },
240            },
241            ValueType::Float => Self::float(Vec::new()),
242            ValueType::Bool => Self::bool(Vec::new()),
243            ValueType::Nil => Self::nil(0),
244            ValueType::Tuple(item_type) => Self {
245                kind: ListValueKind::Tuple {
246                    item_type,
247                    values: Vec::new(),
248                },
249            },
250            ValueType::List(item_type) => Self {
251                kind: ListValueKind::List {
252                    item_type,
253                    values: Vec::new(),
254                },
255            },
256            ValueType::Function(item_type) => Self {
257                kind: ListValueKind::Function {
258                    item_type: *item_type,
259                    values: Vec::new(),
260                },
261            },
262        }
263    }
264
265    pub fn item_type(&self) -> ValueType {
266        match &self.kind {
267            ListValueKind::Parameter(parameter) => ValueType::Parameter(*parameter),
268            ListValueKind::Int(_) => ValueType::Int,
269            ListValueKind::String(_) => ValueType::String,
270            ListValueKind::BitArray(_) => ValueType::BitArray,
271            ListValueKind::UtfCodepoint(_) => ValueType::UtfCodepoint,
272            ListValueKind::Custom { item_type, .. } => ValueType::Custom(item_type.clone()),
273            ListValueKind::External { item_type, .. } => ValueType::External(item_type.clone()),
274            ListValueKind::Float(_) => ValueType::Float,
275            ListValueKind::Bool(_) => ValueType::Bool,
276            ListValueKind::Nil(_) => ValueType::Nil,
277            ListValueKind::Tuple { item_type, .. } => ValueType::Tuple(item_type.clone()),
278            ListValueKind::List { item_type, .. } => ValueType::List(item_type.clone()),
279            ListValueKind::Function { item_type, .. } => {
280                ValueType::Function(Box::new(item_type.clone()))
281            }
282        }
283    }
284
285    pub fn len(&self) -> usize {
286        match &self.kind {
287            ListValueKind::Parameter(_) => 0,
288            ListValueKind::Int(values) => values.len(),
289            ListValueKind::String(values) => values.len(),
290            ListValueKind::BitArray(values) => values.len(),
291            ListValueKind::UtfCodepoint(values) => values.len(),
292            ListValueKind::Custom { values, .. } => values.len(),
293            ListValueKind::External { values, .. } => values.len(),
294            ListValueKind::Float(values) => values.len(),
295            ListValueKind::Bool(values) => values.len(),
296            ListValueKind::Nil(len) => *len,
297            ListValueKind::Tuple { values, .. } => values.len(),
298            ListValueKind::List { values, .. } => values.len(),
299            ListValueKind::Function { values, .. } => values.len(),
300        }
301    }
302
303    pub fn is_empty(&self) -> bool {
304        self.len() == 0
305    }
306
307    pub fn to_values(&self) -> Vec<Value> {
308        match &self.kind {
309            ListValueKind::Parameter(_) => Vec::new(),
310            ListValueKind::Int(values) => values.iter().cloned().map(Value::Int).collect(),
311            ListValueKind::String(values) => values.iter().cloned().map(Value::String).collect(),
312            ListValueKind::BitArray(values) => {
313                values.iter().cloned().map(Value::BitArray).collect()
314            }
315            ListValueKind::UtfCodepoint(values) => {
316                values.iter().copied().map(Value::UtfCodepoint).collect()
317            }
318            ListValueKind::Custom { values, .. } => {
319                values.iter().cloned().map(Value::Custom).collect()
320            }
321            ListValueKind::External { values, .. } => {
322                values.iter().cloned().map(Value::External).collect()
323            }
324            ListValueKind::Float(values) => values.iter().copied().map(Value::Float).collect(),
325            ListValueKind::Bool(values) => values.iter().copied().map(Value::Bool).collect(),
326            ListValueKind::Nil(len) => vec![Value::Nil; *len],
327            ListValueKind::Tuple { values, .. } => {
328                values.iter().cloned().map(Value::Tuple).collect()
329            }
330            ListValueKind::List { values, .. } => values.iter().cloned().map(Value::List).collect(),
331            ListValueKind::Function { values, .. } => {
332                values.iter().cloned().map(Value::Function).collect()
333            }
334        }
335    }
336
337    pub(super) fn kind(&self) -> &ListValueKind {
338        &self.kind
339    }
340}
341
342fn ensure_item_types(
343    expected: &ValueType,
344    actual: impl IntoIterator<Item = ValueType>,
345) -> Result<(), ListValueItemTypeMismatch> {
346    for (index, actual) in actual.into_iter().enumerate() {
347        if actual != *expected {
348            return Err(ListValueItemTypeMismatch {
349                index,
350                expected: expected.clone(),
351                actual,
352            });
353        }
354    }
355
356    Ok(())
357}
358
359#[cfg(test)]
360mod tests {
361    use super::{ListValue, ListValueItemTypeMismatch};
362    use crate::host::HostExternalStore;
363    use crate::plan::{
364        CustomType, CustomTypeName, ExternalType, ExternalTypeName, FunctionType, TypeParameterId,
365        ValueType,
366    };
367    use crate::runtime::{BitArrayValue, CustomValue, ExternalValue, FunctionValue, Value};
368
369    #[test]
370    fn list_value_operations_preserve_every_storage_family() {
371        let function = sample_function();
372        let function_type = function.type_();
373        let custom_type = sample_custom_type("Boxed");
374        let custom = sample_custom_value(custom_type.clone(), "Boxed");
375        let external_type = sample_external_type("Resource");
376        let first_external = sample_external_value(external_type.clone(), 1);
377        let second_external = sample_external_value(external_type.clone(), 2);
378        let cases = [
379            (
380                ListValue::int(vec![1.into(), 2.into()]),
381                ValueType::Int,
382                vec![Value::Int(1.into()), Value::Int(2.into())],
383            ),
384            (
385                ListValue::string(vec!["one".into(), "two".into()]),
386                ValueType::String,
387                vec![Value::String("one".into()), Value::String("two".into())],
388            ),
389            (
390                ListValue::bit_array(vec![
391                    BitArrayValue::from_bytes(vec![1]),
392                    BitArrayValue::from_bytes(vec![2]),
393                ]),
394                ValueType::BitArray,
395                vec![
396                    Value::BitArray(BitArrayValue::from_bytes(vec![1])),
397                    Value::BitArray(BitArrayValue::from_bytes(vec![2])),
398                ],
399            ),
400            (
401                ListValue::utf_codepoint(vec!['a', '\u{10ffff}']),
402                ValueType::UtfCodepoint,
403                vec![Value::UtfCodepoint('a'), Value::UtfCodepoint('\u{10ffff}')],
404            ),
405            (
406                ListValue::from_evaluated_custom(
407                    custom_type.clone(),
408                    vec![custom.clone(), custom.clone()],
409                ),
410                ValueType::Custom(custom_type.clone()),
411                vec![Value::Custom(custom.clone()), Value::Custom(custom.clone())],
412            ),
413            (
414                ListValue::from_evaluated_external(
415                    external_type.clone(),
416                    vec![first_external.clone(), second_external.clone()],
417                ),
418                ValueType::External(external_type.clone()),
419                vec![
420                    Value::External(first_external),
421                    Value::External(second_external),
422                ],
423            ),
424            (
425                ListValue::float(vec![1.5, 2.5]),
426                ValueType::Float,
427                vec![Value::Float(1.5), Value::Float(2.5)],
428            ),
429            (
430                ListValue::bool(vec![true, false]),
431                ValueType::Bool,
432                vec![Value::Bool(true), Value::Bool(false)],
433            ),
434            (
435                ListValue::nil(2),
436                ValueType::Nil,
437                vec![Value::Nil, Value::Nil],
438            ),
439            (
440                ListValue::from_evaluated_tuple(
441                    vec![ValueType::Int],
442                    vec![vec![Value::Int(1.into())], vec![Value::Int(2.into())]],
443                ),
444                ValueType::Tuple(vec![ValueType::Int]),
445                vec![
446                    Value::Tuple(vec![Value::Int(1.into())]),
447                    Value::Tuple(vec![Value::Int(2.into())]),
448                ],
449            ),
450            (
451                ListValue::from_evaluated_list(
452                    ValueType::Int,
453                    vec![
454                        ListValue::int(vec![1.into()]),
455                        ListValue::int(vec![2.into()]),
456                    ],
457                ),
458                ValueType::List(Box::new(ValueType::Int)),
459                vec![
460                    Value::List(ListValue::int(vec![1.into()])),
461                    Value::List(ListValue::int(vec![2.into()])),
462                ],
463            ),
464            (
465                ListValue::from_evaluated_function(
466                    function_type.clone(),
467                    vec![function.clone(), function.clone()],
468                ),
469                ValueType::Function(Box::new(function_type.clone())),
470                vec![
471                    Value::Function(function.clone()),
472                    Value::Function(function.clone()),
473                ],
474            ),
475        ];
476
477        for (value, item_type, expected) in cases {
478            assert_eq!(value.item_type(), item_type);
479            assert_eq!(value.len(), 2);
480            assert!(!value.is_empty());
481            assert_eq!(value.to_values(), expected);
482        }
483
484        for item_type in [
485            ValueType::Parameter(TypeParameterId(0)),
486            ValueType::Int,
487            ValueType::String,
488            ValueType::BitArray,
489            ValueType::UtfCodepoint,
490            ValueType::Custom(custom_type),
491            ValueType::External(external_type),
492            ValueType::Float,
493            ValueType::Bool,
494            ValueType::Nil,
495            ValueType::Tuple(vec![ValueType::Int]),
496            ValueType::List(Box::new(ValueType::Int)),
497            ValueType::Function(Box::new(function_type)),
498        ] {
499            let value = ListValue::empty(item_type.clone());
500            assert_eq!(value.item_type(), item_type);
501            assert_eq!(value.len(), 0);
502            assert!(value.is_empty());
503            assert_eq!(value.to_values(), Vec::<Value>::new());
504        }
505    }
506
507    #[test]
508    fn checked_list_value_constructors_report_exact_item_mismatches() {
509        let expected_custom_type = sample_custom_type("Expected");
510        let actual_custom_type = sample_custom_type("Actual");
511        let expected_custom_value = sample_custom_value(expected_custom_type.clone(), "Expected");
512        let actual_custom_value = sample_custom_value(actual_custom_type.clone(), "Actual");
513        let expected_external_type = sample_external_type("ExpectedResource");
514        let actual_external_type = sample_external_type("ActualResource");
515        let expected_external_value = sample_external_value(expected_external_type.clone(), 1);
516        let actual_external_value = sample_external_value(actual_external_type.clone(), 2);
517
518        assert_eq!(
519            ListValue::try_custom(
520                expected_custom_type.clone(),
521                vec![expected_custom_value.clone(), actual_custom_value],
522            ),
523            Err(ListValueItemTypeMismatch {
524                index: 1,
525                expected: ValueType::Custom(expected_custom_type.clone()),
526                actual: ValueType::Custom(actual_custom_type),
527            }),
528        );
529        assert_eq!(
530            ListValue::try_tuple(
531                vec![ValueType::Int],
532                vec![
533                    vec![Value::Int(1.into())],
534                    vec![Value::String("wrong".into())],
535                ],
536            ),
537            Err(ListValueItemTypeMismatch {
538                index: 1,
539                expected: ValueType::Tuple(vec![ValueType::Int]),
540                actual: ValueType::Tuple(vec![ValueType::String]),
541            }),
542        );
543        assert_eq!(
544            ListValue::try_external(
545                expected_external_type.clone(),
546                vec![expected_external_value.clone(), actual_external_value],
547            ),
548            Err(ListValueItemTypeMismatch {
549                index: 1,
550                expected: ValueType::External(expected_external_type.clone()),
551                actual: ValueType::External(actual_external_type),
552            }),
553        );
554        assert_eq!(
555            ListValue::try_list(
556                ValueType::Int,
557                vec![ListValue::int(Vec::new()), ListValue::string(Vec::new())],
558            ),
559            Err(ListValueItemTypeMismatch {
560                index: 1,
561                expected: ValueType::List(Box::new(ValueType::Int)),
562                actual: ValueType::List(Box::new(ValueType::String)),
563            }),
564        );
565
566        let function = sample_function();
567        assert_eq!(
568            ListValue::try_function(
569                FunctionType::new(vec![ValueType::Int], ValueType::Int),
570                vec![function],
571            ),
572            Err(ListValueItemTypeMismatch {
573                index: 0,
574                expected: ValueType::Function(Box::new(FunctionType::new(
575                    vec![ValueType::Int],
576                    ValueType::Int,
577                ))),
578                actual: ValueType::Function(Box::new(FunctionType::new(
579                    Vec::new(),
580                    ValueType::Int,
581                ))),
582            }),
583        );
584
585        assert_eq!(
586            ListValue::try_custom(
587                expected_custom_type.clone(),
588                vec![expected_custom_value.clone()],
589            ),
590            Ok(ListValue::from_evaluated_custom(
591                expected_custom_type.clone(),
592                vec![expected_custom_value],
593            )),
594        );
595        assert_eq!(
596            ListValue::try_custom(expected_custom_type.clone(), Vec::new()),
597            Ok(ListValue::from_evaluated_custom(
598                expected_custom_type,
599                Vec::new(),
600            )),
601        );
602        assert_eq!(
603            ListValue::try_external(
604                expected_external_type.clone(),
605                vec![expected_external_value.clone()],
606            ),
607            Ok(ListValue::from_evaluated_external(
608                expected_external_type.clone(),
609                vec![expected_external_value],
610            )),
611        );
612        assert_eq!(
613            ListValue::try_external(expected_external_type.clone(), Vec::new()),
614            Ok(ListValue::from_evaluated_external(
615                expected_external_type,
616                Vec::new(),
617            )),
618        );
619        assert_eq!(
620            ListValue::try_tuple(vec![ValueType::Int], Vec::new()),
621            Ok(ListValue::from_evaluated_tuple(
622                vec![ValueType::Int],
623                Vec::new()
624            )),
625        );
626        assert_eq!(
627            ListValue::try_list(ValueType::Int, Vec::new()),
628            Ok(ListValue::from_evaluated_list(ValueType::Int, Vec::new())),
629        );
630        assert_eq!(
631            ListValue::try_function(FunctionType::new(Vec::new(), ValueType::Int), Vec::new()),
632            Ok(ListValue::from_evaluated_function(
633                FunctionType::new(Vec::new(), ValueType::Int),
634                Vec::new(),
635            )),
636        );
637    }
638
639    #[test]
640    fn checked_function_list_value_constructor_accepts_matching_non_empty_values() {
641        let function = sample_function();
642        let function_type = function.type_();
643
644        assert_eq!(
645            ListValue::try_function(function_type.clone(), vec![function.clone()]),
646            Ok(ListValue::from_evaluated_function(
647                function_type,
648                vec![function],
649            )),
650        );
651    }
652
653    fn sample_function() -> FunctionValue {
654        FunctionValue::new(
655            crate::plan::execution::function::RuntimeFunctionId::Core(
656                crate::plan::execution::function::CoreRuntimeFunctionId::Int(
657                    crate::plan::execution::function::IntFunctionId(0),
658                ),
659            ),
660            Vec::new(),
661            FunctionType::new(Vec::new(), ValueType::Int),
662        )
663    }
664
665    fn sample_custom_type(name: &str) -> CustomType {
666        CustomType::new(
667            CustomTypeName::new("geam".into(), "main".into(), name.into()),
668            Vec::new(),
669        )
670    }
671
672    fn sample_custom_value(type_: CustomType, constructor_name: &str) -> CustomValue {
673        CustomValue::from_evaluated(type_, constructor_name.into(), 0, Vec::new())
674    }
675
676    fn sample_external_type(name: &str) -> ExternalType {
677        ExternalType::new(
678            ExternalTypeName::new("geam".into(), "main".into(), name.into()),
679            Vec::new(),
680        )
681    }
682
683    fn sample_external_value(type_: ExternalType, payload: usize) -> ExternalValue {
684        fn source_hash(
685            context: &crate::host::HostExternalHashing<'_>,
686            value: &crate::host::HostStoredValue<num_bigint::BigInt>,
687        ) -> u64 {
688            context.stored_value_hash(value)
689        }
690
691        fn inspect(
692            context: &crate::host::HostExternalInspection<'_>,
693            value: &crate::host::HostStoredValue<num_bigint::BigInt>,
694        ) -> ecow::EcoString {
695            context.inspect_stored_value(value)
696        }
697
698        let store = HostExternalStore::default();
699        let source_equal =
700            |context: &crate::host::HostExternalEquality<'_>,
701             left: &crate::host::HostStoredValue<num_bigint::BigInt>,
702             right: &crate::host::HostStoredValue<num_bigint::BigInt>| {
703                context.stored_values_equal(left, right)
704            };
705        let first = store.insert(
706            crate::host::HostStoredValue::new(crate::runtime::StoredRuntimeValue::test_int(
707                payload.into(),
708            )),
709            source_equal,
710            source_hash,
711            inspect,
712        );
713        let equal = store.insert(
714            crate::host::HostStoredValue::new(crate::runtime::StoredRuntimeValue::test_int(
715                payload.into(),
716            )),
717            source_equal,
718            source_hash,
719            inspect,
720        );
721        let stored_equal =
722            |left: &crate::runtime::StoredRuntimeValue,
723             right: &crate::runtime::StoredRuntimeValue| left.value() == right.value();
724        let equality = crate::host::HostExternalEquality::new(&stored_equal);
725        assert!(first.source_equal(&equality, &equal));
726        let stored_hash = |_: &crate::runtime::StoredRuntimeValue| payload as u64;
727        let stored_inspect =
728            |_: &crate::runtime::StoredRuntimeValue| format!("Resource({payload})").into();
729        assert_eq!(
730            first.source_hash(&crate::host::HostExternalHashing::new(&stored_hash)),
731            payload as u64,
732        );
733        let expected_inspection = format!("Resource({payload})");
734        assert_eq!(
735            first
736                .inspection(&crate::host::HostExternalInspection::new(&stored_inspect))
737                .as_str(),
738            expected_inspection,
739        );
740        ExternalValue::from_evaluated(type_, first, format!("Resource({payload})").into())
741    }
742}