1pub(crate) use geam_core::{
2 BitArrayValue, HostCall, HostComponentProfile, HostConstruction, HostExternal,
3 HostExternalType, HostFailure, HostProfile, HostProvider, HostProviderComponent,
4 HostProviderComponentRegistration, HostProviderModule, HostRegistrationError, HostType,
5 HostTypeIndex0, HostTypeIndexNext, HostTypeList, HostTypeListEnd,
6};
7#[cfg(test)]
8pub(crate) use geam_core::{
9 ExecutionError, HostCallCompletion, HostCallError, HostExternalBinding, HostExternalEquality,
10 HostExternalHashing, HostExternalInspection, HostExternalSchema, HostExternalStorage,
11 HostExternalStore, HostExternalTypeSchema, HostModule, HostProviderSet, HostedExecution,
12 ModuleSource, PackageSource, PanicKind, PanicMessage, Value, ValueType,
13 compile_typed_host_program, plan_host_program,
14};
15use std::marker::PhantomData;
16
17mod bit_array;
18mod dict;
19mod dynamic;
20mod dynamic_decode;
21mod float;
22mod int;
23mod io;
24mod result;
25mod run_state;
26mod string;
27mod string_tree;
28mod uri;
29
30pub use io::{IoOutput, IoSink, IoStream};
31pub use run_state::{GleamStdlibRunState, GleamStdlibRunStateError};
32
33#[doc(hidden)]
35pub mod provider_support {
36 pub use crate::dict::{DictExternalStorage, DictOf, DictSchema, create_dynamic_dict};
37 pub use crate::dynamic::{
38 Dynamic, DynamicExternalStorage, DynamicSchema, create_value as create_dynamic_value,
39 };
40 pub use crate::dynamic_decode::DynamicDecodeErrorValue;
41 pub use crate::result::{GleamError, GleamOk, GleamResult};
42 pub use crate::string_tree::{
43 StoredStringTree, StringTree, StringTreeExternalStorage, StringTreePayload,
44 StringTreeSchema,
45 };
46}
47
48pub trait GleamStdlibHostProfile: HostComponentProfile<Component<Self::Io>> {
50 type Io: IoSink + 'static;
52}
53
54#[derive(Default)]
56pub struct GleamStdlibStores {
57 dict: dict::Stores,
58 dynamic: dynamic::Stores,
59 string_tree: string_tree::Stores,
60}
61
62#[derive(Debug, Clone, Copy)]
64pub struct Component<Io = Vec<IoOutput>>(PhantomData<fn() -> Io>);
65
66impl<Io> HostProviderComponent for Component<Io>
67where
68 Io: IoSink + 'static,
69{
70 const ID: &'static str = "gleam_stdlib";
71 type Stores = GleamStdlibStores;
72 type RunState = GleamStdlibRunState<Io>;
73}
74
75impl<Io> geam_core::__macro_support::ProviderPackage for Component<Io>
76where
77 Io: IoSink + 'static,
78{
79 const PACKAGE: &'static str = "gleam_stdlib";
80}
81
82#[derive(Debug, Clone, Copy)]
84pub struct GleamStdlibProfile;
85
86impl HostProfile for GleamStdlibProfile {
87 type RunState = GleamStdlibRunState;
88 type ExternalStores = GleamStdlibStores;
89}
90
91impl HostComponentProfile<Component> for GleamStdlibProfile {
92 fn component_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
93 stores
94 }
95
96 fn component_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
97 state
98 }
99}
100
101impl GleamStdlibHostProfile for GleamStdlibProfile {
102 type Io = Vec<IoOutput>;
103}
104
105pub fn host_providers<Profile>() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError>
107where
108 Profile: GleamStdlibHostProfile,
109{
110 <Component<Profile::Io> as HostProviderComponentRegistration<Profile>>::providers()
111}
112
113impl<Profile, Io> HostProviderComponentRegistration<Profile> for Component<Io>
114where
115 Profile: GleamStdlibHostProfile<Io = Io>,
116 Io: IoSink + 'static,
117{
118 fn providers() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError> {
119 register_host_providers::<Profile>()
120 }
121}
122
123pub(crate) fn stdlib_stores<Profile>(stores: &Profile::ExternalStores) -> &GleamStdlibStores
124where
125 Profile: GleamStdlibHostProfile,
126{
127 <Profile as HostComponentProfile<Component<Profile::Io>>>::component_stores(stores)
128}
129
130fn register_host_providers<Profile>()
131-> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError>
132where
133 Profile: GleamStdlibHostProfile,
134{
135 let registrations: [ProviderRegistration<Profile>; 10] = [
136 dict::host_provider::<Profile>,
137 dynamic::host_provider::<Profile>,
138 float::host_provider::<Profile>,
139 int::host_provider::<Profile>,
140 string_tree::host_provider::<Profile>,
141 string::host_provider::<Profile>,
142 bit_array::host_provider::<Profile>,
143 dynamic_decode::host_provider::<Profile>,
144 io::host_provider::<Profile>,
145 uri::host_provider::<Profile>,
146 ];
147
148 registrations
149 .into_iter()
150 .map(|register| register())
151 .collect()
152}
153
154type ProviderRegistration<Profile> =
155 fn() -> Result<HostProviderModule<Profile>, HostRegistrationError>;
156
157#[cfg(test)]
158mod tests {
159 use super::{
160 Component, GleamStdlibHostProfile, GleamStdlibProfile, GleamStdlibRunState,
161 GleamStdlibStores, IoOutput, IoSink, IoStream, host_providers, stdlib_stores,
162 };
163 use crate::{
164 HostComponentProfile, HostProfile, HostProviderComponent, HostProviderComponentRegistration,
165 };
166
167 struct CustomProfile;
168
169 #[derive(Default)]
170 struct CustomStores {
171 stdlib: GleamStdlibStores,
172 }
173
174 struct CustomRunState {
175 stdlib: GleamStdlibRunState<RecordingSink>,
176 }
177
178 #[derive(Default)]
179 struct RecordingSink {
180 outputs: Vec<IoOutput>,
181 }
182
183 impl IoSink for RecordingSink {
184 fn emit(&mut self, output: IoOutput) {
185 self.outputs.push(output);
186 }
187 }
188
189 impl HostProfile for CustomProfile {
190 type RunState = CustomRunState;
191 type ExternalStores = CustomStores;
192 }
193
194 impl HostComponentProfile<Component<RecordingSink>> for CustomProfile {
195 fn component_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
196 &stores.stdlib
197 }
198
199 fn component_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState<RecordingSink> {
200 &mut state.stdlib
201 }
202 }
203
204 impl GleamStdlibHostProfile for CustomProfile {
205 type Io = RecordingSink;
206 }
207
208 #[test]
209 fn registers_providers_in_dependency_first_module_order() {
210 assert_eq!(<Component as HostProviderComponent>::ID, "gleam_stdlib");
211 let providers =
212 <Component as HostProviderComponentRegistration<GleamStdlibProfile>>::providers()
213 .expect("stdlib component should register");
214 let facade = host_providers::<GleamStdlibProfile>()
215 .expect("official stdlib provider facade should register");
216 assert_eq!(
217 facade
218 .iter()
219 .map(|provider| provider.module().as_str())
220 .collect::<Vec<_>>(),
221 providers
222 .iter()
223 .map(|provider| provider.module().as_str())
224 .collect::<Vec<_>>(),
225 );
226
227 assert_eq!(
228 providers
229 .iter()
230 .map(|provider| provider.module().as_str())
231 .collect::<Vec<_>>(),
232 [
233 "gleam/dict",
234 "gleam/dynamic",
235 "gleam/float",
236 "gleam/int",
237 "gleam/string_tree",
238 "gleam/string",
239 "gleam/bit_array",
240 "gleam/dynamic/decode",
241 "gleam/io",
242 "gleam/uri",
243 ],
244 );
245 let provider = &providers[0];
246 assert_eq!(provider.package(), "gleam_stdlib");
247 assert_eq!(provider.module(), "gleam/dict");
248 assert_eq!(
249 provider
250 .external_types()
251 .map(|schema| {
252 (
253 schema.package().as_str(),
254 schema.module().as_str(),
255 schema.name().as_str(),
256 schema.parameter_count(),
257 )
258 })
259 .collect::<Vec<_>>(),
260 [
261 ("gleam_stdlib", "gleam/dict", "Dict", 2),
262 ("gleam_stdlib", "gleam/dict", "TransientDict", 2),
263 ],
264 );
265 assert_eq!(
266 provider
267 .functions()
268 .map(|function| function.name().as_str())
269 .collect::<Vec<_>>(),
270 [
271 "to_transient",
272 "from_transient",
273 "size",
274 "do_has_key",
275 "new",
276 "get",
277 "do_insert",
278 "transient_insert",
279 "do_map_values",
280 "transient_delete",
281 "do_fold",
282 "transient_update_with",
283 ],
284 );
285 }
286
287 #[test]
288 fn custom_profiles_project_stdlib_stores_state_and_io() {
289 let default_stores = GleamStdlibStores::default();
290 let stores = CustomStores::default();
291 let mut default_state = GleamStdlibRunState::from_seed([1; 32]);
292 let mut state = CustomRunState {
293 stdlib: GleamStdlibRunState::from_seed_with_io([2; 32], RecordingSink::default()),
294 };
295
296 assert!(std::ptr::eq(
297 stdlib_stores::<GleamStdlibProfile>(&default_stores),
298 &default_stores,
299 ));
300 assert!(std::ptr::eq(
301 stdlib_stores::<CustomProfile>(&stores),
302 &stores.stdlib,
303 ));
304 let default_state_pointer = &mut default_state as *mut GleamStdlibRunState;
305 assert!(std::ptr::eq(
306 <GleamStdlibProfile as HostComponentProfile<Component>>::component_state(
307 &mut default_state,
308 ),
309 default_state_pointer,
310 ));
311 let state_pointer = &mut state.stdlib as *mut GleamStdlibRunState<RecordingSink>;
312 assert!(std::ptr::eq(
313 <CustomProfile as HostComponentProfile<Component<RecordingSink>>>::component_state(
314 &mut state,
315 ),
316 state_pointer,
317 ));
318
319 let default_io = <GleamStdlibProfile as HostComponentProfile<Component>>::component_state(
320 &mut default_state,
321 )
322 .io_sink();
323 default_io.emit(IoOutput::new(IoStream::Stdout, "default".into()));
324 assert_eq!(default_state.io_outputs()[0].text(), "default");
325
326 let custom_io =
327 <CustomProfile as HostComponentProfile<Component<RecordingSink>>>::component_state(
328 &mut state,
329 )
330 .io_sink();
331 custom_io.emit(IoOutput::new(IoStream::Stderr, "custom".into()));
332 assert_eq!(state.stdlib.io_sink().outputs[0].stream(), IoStream::Stderr);
333 assert_eq!(state.stdlib.io_sink().outputs[0].text(), "custom");
334 }
335}