use crate::{HostProfile, HostProviderModule, HostRegistrationError};
mod bit_array;
mod dict;
mod dynamic;
mod dynamic_decode;
mod float;
mod int;
mod io;
mod option;
mod result;
mod run_state;
mod string;
mod string_tree;
mod uri;
pub use io::{IoOutput, IoSink, IoStream};
pub use run_state::{GleamStdlibRunState, GleamStdlibRunStateError};
pub(crate) use dict::{DictExternalStorage, DictOf, DictSchema, create_dynamic_dict};
pub(crate) use dynamic::{
Dynamic, DynamicExternalStorage, DynamicSchema, create_value as create_dynamic_value,
};
pub(crate) use dynamic_decode::DynamicDecodeError;
pub(crate) use result::{GleamError, GleamOk, GleamResult};
pub(crate) use string_tree::{
StoredStringTree, StringTree, StringTreeExternalStorage, StringTreePayload, StringTreeSchema,
};
pub trait GleamStdlibHostProfile: HostProfile {
type Io: IoSink;
fn gleam_stdlib_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores;
fn gleam_stdlib_run_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState;
fn gleam_stdlib_io(state: &mut Self::RunState) -> &mut Self::Io;
}
#[derive(Default)]
pub struct GleamStdlibStores {
dict: dict::Stores,
dynamic: dynamic::Stores,
string_tree: string_tree::Stores,
}
#[derive(Debug, Clone, Copy)]
pub struct GleamStdlibProfile;
impl HostProfile for GleamStdlibProfile {
type RunState = GleamStdlibRunState;
type ExternalStores = GleamStdlibStores;
}
impl GleamStdlibHostProfile for GleamStdlibProfile {
type Io = Vec<IoOutput>;
fn gleam_stdlib_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
stores
}
fn gleam_stdlib_run_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
state
}
fn gleam_stdlib_io(state: &mut Self::RunState) -> &mut Self::Io {
state.io_sink()
}
}
pub fn host_providers<Profile>() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError>
where
Profile: GleamStdlibHostProfile,
{
let registrations: [ProviderRegistration<Profile>; 10] = [
dict::host_provider::<Profile>,
dynamic::host_provider::<Profile>,
float::host_provider::<Profile>,
int::host_provider::<Profile>,
string_tree::host_provider::<Profile>,
string::host_provider::<Profile>,
bit_array::host_provider::<Profile>,
dynamic_decode::host_provider::<Profile>,
io::host_provider::<Profile>,
uri::host_provider::<Profile>,
];
registrations
.into_iter()
.map(|register| register())
.collect()
}
type ProviderRegistration<Profile> =
fn() -> Result<HostProviderModule<Profile>, HostRegistrationError>;
#[cfg(test)]
mod tests {
use super::{
GleamStdlibHostProfile, GleamStdlibProfile, GleamStdlibRunState, GleamStdlibStores,
IoOutput, IoSink, IoStream, host_providers,
};
use crate::HostProfile;
struct CustomProfile;
#[derive(Default)]
struct CustomStores {
stdlib: GleamStdlibStores,
}
struct CustomRunState {
stdlib: GleamStdlibRunState,
io: RecordingSink,
}
#[derive(Default)]
struct RecordingSink {
outputs: Vec<IoOutput>,
}
impl IoSink for RecordingSink {
fn emit(&mut self, output: IoOutput) {
self.outputs.push(output);
}
}
impl HostProfile for CustomProfile {
type RunState = CustomRunState;
type ExternalStores = CustomStores;
}
impl GleamStdlibHostProfile for CustomProfile {
type Io = RecordingSink;
fn gleam_stdlib_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
&stores.stdlib
}
fn gleam_stdlib_run_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
&mut state.stdlib
}
fn gleam_stdlib_io(state: &mut Self::RunState) -> &mut Self::Io {
&mut state.io
}
}
#[test]
fn registers_providers_in_dependency_first_module_order() {
let providers = host_providers::<GleamStdlibProfile>()
.expect("official stdlib providers should register");
assert_eq!(
providers
.iter()
.map(|provider| provider.module().as_str())
.collect::<Vec<_>>(),
[
"gleam/dict",
"gleam/dynamic",
"gleam/float",
"gleam/int",
"gleam/string_tree",
"gleam/string",
"gleam/bit_array",
"gleam/dynamic/decode",
"gleam/io",
"gleam/uri",
],
);
let provider = &providers[0];
assert_eq!(provider.package(), "gleam_stdlib");
assert_eq!(provider.module(), "gleam/dict");
assert_eq!(
provider
.external_types()
.map(|schema| {
(
schema.package().as_str(),
schema.module().as_str(),
schema.name().as_str(),
schema.parameter_count(),
)
})
.collect::<Vec<_>>(),
[
("gleam_stdlib", "gleam/dict", "Dict", 2),
("gleam_stdlib", "gleam/dict", "TransientDict", 2),
],
);
assert_eq!(
provider
.functions()
.map(|function| function.name().as_str())
.collect::<Vec<_>>(),
[
"to_transient",
"from_transient",
"size",
"do_has_key",
"new",
"get",
"do_insert",
"transient_insert",
"do_map_values",
"transient_delete",
"do_fold",
"transient_update_with",
],
);
}
#[test]
fn custom_profiles_project_stdlib_stores_state_and_io() {
let default_stores = GleamStdlibStores::default();
let stores = CustomStores::default();
let mut default_state = GleamStdlibRunState::from_seed([1; 32]);
let mut state = CustomRunState {
stdlib: GleamStdlibRunState::from_seed([2; 32]),
io: RecordingSink::default(),
};
assert!(std::ptr::eq(
GleamStdlibProfile::gleam_stdlib_stores(&default_stores),
&default_stores,
));
assert!(std::ptr::eq(
CustomProfile::gleam_stdlib_stores(&stores),
&stores.stdlib,
));
let default_projected = GleamStdlibProfile::gleam_stdlib_run_state(&mut default_state);
assert!(std::ptr::eq(default_projected, &default_state));
let projected = CustomProfile::gleam_stdlib_run_state(&mut state);
assert!(std::ptr::eq(projected, &state.stdlib));
let default_io = GleamStdlibProfile::gleam_stdlib_io(&mut default_state);
default_io.emit(IoOutput::new(IoStream::Stdout, "default".into()));
assert_eq!(default_state.io_outputs()[0].text(), "default");
let custom_io = CustomProfile::gleam_stdlib_io(&mut state);
custom_io.emit(IoOutput::new(IoStream::Stderr, "custom".into()));
assert_eq!(state.io.outputs[0].stream(), IoStream::Stderr);
assert_eq!(state.io.outputs[0].text(), "custom");
}
}