geam_core/host/external/
dynamic.rs1use super::stored::{HostExternalPayloadBuilder, HostExternalPayloadView};
2use crate::host::{HostCall, HostProfile, HostProvider, HostType, HostTypeSequence};
3use crate::provider_support::HostStoredValueFamily;
4
5pub struct HostStoredDynamic {
39 value: crate::runtime::StoredRuntimeValue,
40}
41
42impl HostStoredDynamic {
43 pub(crate) fn new(value: crate::runtime::StoredRuntimeValue) -> Self {
44 Self { value }
45 }
46
47 pub(crate) fn value_family(&self) -> HostStoredValueFamily {
48 self.value.family()
49 }
50
51 pub(crate) fn has_external_schema<Schema>(&self) -> bool
52 where
53 Schema: crate::host::HostExternalSchema,
54 {
55 let crate::plan::ValueType::External(type_) = self.value.type_() else {
56 return false;
57 };
58 let name = type_.type_name();
59 name.package() == Schema::PACKAGE
60 && name.module() == Schema::MODULE
61 && name.name() == Schema::NAME
62 && type_.arguments().len() == Schema::PARAMETER_COUNT
63 }
64
65 #[expect(
66 clippy::result_large_err,
67 reason = "non-tuples retain the original value without another heap allocation"
68 )]
69 pub(crate) fn map_tuple_items<Item>(
70 self,
71 mut map: impl FnMut(Self) -> Item,
72 ) -> Result<Box<[Item]>, Self> {
73 self.value
74 .map_tuple_items(|value| map(Self::new(value)))
75 .map_err(Self::new)
76 }
77
78 pub(super) fn runtime_value(&self) -> &crate::runtime::StoredRuntimeValue {
79 &self.value
80 }
81
82 pub(crate) fn decode<'call, Profile, Provider, Return, Type>(
83 &self,
84 call: &mut HostCall<'call, Profile, Provider, Return>,
85 ) -> Option<Type::Value<'call>>
86 where
87 Profile: HostProfile,
88 Provider: HostProvider<Profile>,
89 Return: HostType,
90 Type: HostType,
91 {
92 let requested = call.resolve_host_type::<Type>()?;
93 if !self.has_type(&requested) {
94 return None;
95 }
96 Some(call.restore_runtime_value::<Type>(&self.value))
97 }
98
99 fn has_type(&self, type_: &crate::plan::ValueType) -> bool {
100 self.value.type_() == type_
101 }
102}
103
104impl<Profile, Arguments> HostExternalPayloadBuilder<'_, Profile, Arguments>
105where
106 Profile: HostProfile,
107 Arguments: HostTypeSequence,
108{
109 pub fn store_dynamic<Type>(&mut self, value: Type::Value<'_>) -> HostStoredDynamic
111 where
112 Type: HostType,
113 {
114 HostStoredDynamic::new(
115 self.runtime
116 .retain_stored(crate::host::type_::into_scoped::<Type>(value)),
117 )
118 }
119}
120
121impl<'call, Payload, Arguments> HostExternalPayloadView<'call, Payload, Arguments>
122where
123 Arguments: HostTypeSequence,
124{
125 pub fn decode<Profile, Provider, Return, Type>(
127 &self,
128 call: &mut HostCall<'call, Profile, Provider, Return>,
129 select: impl FnOnce(&Payload) -> &HostStoredDynamic,
130 ) -> Option<Type::Value<'call>>
131 where
132 Profile: HostProfile,
133 Provider: HostProvider<Profile>,
134 Return: HostType,
135 Type: HostType,
136 {
137 select(&self.value).decode::<Profile, Provider, Return, Type>(call)
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::HostStoredDynamic;
144 use crate::plan::ValueType;
145 use num_bigint::BigInt;
146
147 #[test]
148 fn dynamic_value_matches_only_its_exact_specialized_type() {
149 let stored = HostStoredDynamic::new(crate::runtime::StoredRuntimeValue::test_int(
150 BigInt::from(7),
151 ));
152
153 assert!(stored.has_type(&ValueType::Int));
154 assert!(!stored.has_type(&ValueType::String));
155 }
156}