Skip to main content

geam_core/provider/
stored.rs

1use crate::host::{
2    HostExternalPayloadBuilder, HostProfile, HostStoredType, HostStoredValue, HostType, HostTypeAt,
3    HostTypeSequence,
4};
5use crate::provider::ProviderExternalItem;
6use crate::provider::advanced::{Retained, StoredDynamic};
7use std::marker::PhantomData;
8
9/// Generated identity for one external declaration that may own retained values.
10#[doc(hidden)]
11pub trait ProviderStoredOwner: 'static {}
12
13/// Retains one low-level generic argument for a macro-authored payload.
14///
15/// This bridge exists for built-in providers that still consume the typed-host
16/// SDK while a sibling component has moved to the authoring macros.
17#[doc(hidden)]
18pub fn retain_argument<Profile, Arguments, Owner, Index>(
19    builder: &mut HostExternalPayloadBuilder<'_, Profile, Arguments>,
20    value: <<Arguments as HostTypeAt<Index>>::Type as HostType>::Value<'_>,
21) -> Retained<Owner, Index>
22where
23    Profile: HostProfile,
24    Arguments: HostTypeSequence + HostTypeAt<Index>,
25    Owner: ProviderStoredOwner,
26{
27    Retained::new(builder.store_argument::<Index>(value))
28}
29
30/// Retains one low-level value with its exact specialized type.
31#[doc(hidden)]
32pub fn retain_dynamic<Profile, Arguments, Owner, Type>(
33    builder: &mut HostExternalPayloadBuilder<'_, Profile, Arguments>,
34    value: Type::Value<'_>,
35) -> StoredDynamic<Owner>
36where
37    Profile: HostProfile,
38    Arguments: HostTypeSequence,
39    Owner: ProviderStoredOwner,
40    Type: HostType,
41{
42    StoredDynamic::new(builder.store_dynamic::<Type>(value))
43}
44
45/// One generic Gleam value retained by a macro-authored external payload.
46///
47/// Providers create stored values through [`super::Call::store`] and restore
48/// fields exposed by a generated external input through [`super::Call::restore`].
49/// A stored value is neither cloneable nor copyable and cannot be constructed
50/// independently of its generated external owner.
51pub struct Stored<Type, Context = MissingStoredContext> {
52    context: Context,
53    type_: PhantomData<fn() -> Type>,
54}
55
56#[doc(hidden)]
57pub struct MissingStoredContext;
58
59/// A retained value being assembled into one generated external payload.
60#[doc(hidden)]
61pub struct ProviderStoredOutput<'call, Owner, Index, Host> {
62    value: HostStoredValue<HostStoredType<Index>>,
63    call: PhantomData<&'call ()>,
64    owner: PhantomData<fn() -> (Owner, Host)>,
65}
66
67/// A borrowed retained field selected from one generated external input.
68#[doc(hidden)]
69pub struct ProviderStoredInput<'value, Owner, Index, Host> {
70    value: &'value HostStoredValue<HostStoredType<Index>>,
71    context: PhantomData<fn() -> (Owner, Host)>,
72}
73
74/// The exact retained payload view inserted into one generated external input.
75#[doc(hidden)]
76pub struct ProviderExternalInputContext<'call, Payload, Arguments>
77where
78    Arguments: HostTypeSequence,
79{
80    value: ProviderExternalItem<Payload>,
81    context: PhantomData<&'call Arguments>,
82}
83
84#[doc(hidden)]
85pub struct MissingExternalInputContext;
86
87#[doc(hidden)]
88pub struct ProviderExternalOutput<Payload> {
89    value: Result<Payload, ProviderExternalItem<Payload>>,
90}
91
92#[doc(hidden)]
93pub struct MissingExternalOutputContext;
94
95impl<Type, Owner, Index, Host> Stored<Type, ProviderStoredOutput<'_, Owner, Index, Host>>
96where
97    Owner: ProviderStoredOwner,
98{
99    #[doc(hidden)]
100    pub fn from_output(value: HostStoredValue<HostStoredType<Index>>) -> Self {
101        Self {
102            context: ProviderStoredOutput {
103                value,
104                call: PhantomData,
105                owner: PhantomData,
106            },
107            type_: PhantomData,
108        }
109    }
110
111    #[doc(hidden)]
112    pub fn into_host(self) -> HostStoredValue<HostStoredType<Index>> {
113        self.context.value
114    }
115
116    /// Moves this newly stored value into an advanced persistent payload.
117    pub fn into_retained(self) -> Retained<Owner, Index> {
118        Retained::new(self.context.value)
119    }
120}
121
122impl<'value, Type, Owner, Index, Host> Stored<Type, ProviderStoredInput<'value, Owner, Index, Host>>
123where
124    Owner: ProviderStoredOwner,
125{
126    #[doc(hidden)]
127    pub fn from_input(value: &'value HostStoredValue<HostStoredType<Index>>) -> Self {
128        Self {
129            context: ProviderStoredInput {
130                value,
131                context: PhantomData,
132            },
133            type_: PhantomData,
134        }
135    }
136
137    #[doc(hidden)]
138    pub fn from_retained(value: &'value Retained<Owner, Index>) -> Self {
139        Self::from_input(value.host())
140    }
141
142    pub(crate) fn host(&self) -> &'value HostStoredValue<HostStoredType<Index>> {
143        self.context.value
144    }
145}
146
147impl<Payload> ProviderExternalOutput<Payload> {
148    #[doc(hidden)]
149    pub fn new(payload: Payload) -> Self {
150        Self { value: Ok(payload) }
151    }
152
153    #[doc(hidden)]
154    pub fn from_input(value: ProviderExternalItem<Payload>) -> Self {
155        Self { value: Err(value) }
156    }
157
158    #[doc(hidden)]
159    pub fn into_value(self) -> Result<Payload, ProviderExternalItem<Payload>> {
160        self.value
161    }
162}
163
164impl<'call, Payload, Arguments> ProviderExternalInputContext<'call, Payload, Arguments>
165where
166    Arguments: HostTypeSequence,
167{
168    #[doc(hidden)]
169    pub fn from_host(value: ProviderExternalItem<Payload>) -> Self {
170        Self {
171            value,
172            context: PhantomData,
173        }
174    }
175
176    #[doc(hidden)]
177    pub fn payload(&self) -> &Payload {
178        &self.value
179    }
180
181    #[doc(hidden)]
182    pub fn into_output(self) -> ProviderExternalOutput<Payload> {
183        ProviderExternalOutput::from_input(self.value)
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::{ProviderStoredOwner, retain_argument, retain_dynamic};
190    use crate::host::test::{TestHostCallRuntime, TestHostProfile, TestRunState};
191    use crate::host::{
192        CallArguments, HostExternalPayloadBuilder, HostTypeIndex0, HostTypeList, HostTypeListEnd,
193    };
194    use crate::plan::ValueType;
195    use crate::provider::advanced::{DynamicKind, Retained, StoredDynamic};
196    use num_bigint::BigInt;
197
198    struct Payload;
199
200    impl ProviderStoredOwner for Payload {}
201
202    #[test]
203    fn retention_bridges_preserve_the_declared_owner_and_runtime_type() {
204        type Arguments = HostTypeList<BigInt, HostTypeListEnd>;
205
206        let mut state = TestRunState::default();
207        let arguments = CallArguments::new(Vec::new(), Vec::new());
208        let mut runtime = TestHostCallRuntime::new(&mut state, arguments);
209        let mut builder =
210            HostExternalPayloadBuilder::<TestHostProfile, Arguments>::new(&mut runtime);
211
212        let retained: Retained<Payload, HostTypeIndex0> =
213            retain_argument(&mut builder, BigInt::from(7));
214        let dynamic: StoredDynamic<Payload> =
215            retain_dynamic::<_, Arguments, _, BigInt>(&mut builder, BigInt::from(8));
216
217        assert_eq!(retained.host().value.type_(), &ValueType::Int);
218        assert_eq!(dynamic.kind(), DynamicKind::Int);
219    }
220}