mod function;
mod schema;
mod storage;
use self::function::{
JsonProvider, decode_to_dynamic, do_bool, do_float, do_int, do_null, do_object,
do_preprocessed_array, do_string, do_to_string, to_string_tree,
};
use self::schema::{DecodeConstructions, Json, JsonDynamicResult, JsonList, ObjectEntries};
use crate::gleam_stdlib::{
Component as GleamStdlibComponent, GleamStdlibHostProfile, GleamStdlibRunState,
GleamStdlibStores, IoOutput,
};
use crate::{
BitArrayValue, HostComponentProfile, HostProfile, HostProviderComponent,
HostProviderComponentRegistration, HostProviderModule, HostRegistrationError,
};
use ecow::EcoString;
use num_bigint::BigInt;
pub trait GleamJsonHostProfile: GleamStdlibHostProfile + HostComponentProfile<Component> {}
impl<Profile> GleamJsonHostProfile for Profile where
Profile: GleamStdlibHostProfile + HostComponentProfile<Component>
{
}
#[derive(Default)]
pub struct GleamJsonStores {
json: storage::Stores,
}
#[derive(Debug, Clone, Copy)]
pub struct Component;
impl HostProviderComponent for Component {
const ID: &'static str = "gleam_json";
type Stores = GleamJsonStores;
type RunState = ();
}
#[derive(Default)]
pub struct GleamJsonProfileStores {
stdlib: GleamStdlibStores,
json: GleamJsonStores,
}
pub struct GleamJsonRunState {
stdlib: GleamStdlibRunState,
json: (),
}
#[derive(Debug, Clone, Copy)]
pub struct GleamJsonProfile;
impl GleamJsonRunState {
pub fn new(stdlib: GleamStdlibRunState) -> Self {
Self { stdlib, json: () }
}
pub fn stdlib(&self) -> &GleamStdlibRunState {
&self.stdlib
}
pub fn stdlib_mut(&mut self) -> &mut GleamStdlibRunState {
&mut self.stdlib
}
}
impl HostProfile for GleamJsonProfile {
type RunState = GleamJsonRunState;
type ExternalStores = GleamJsonProfileStores;
}
impl HostComponentProfile<GleamStdlibComponent> for GleamJsonProfile {
fn component_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
&stores.stdlib
}
fn component_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
&mut state.stdlib
}
}
impl GleamStdlibHostProfile for GleamJsonProfile {
type Io = Vec<IoOutput>;
}
impl HostComponentProfile<Component> for GleamJsonProfile {
fn component_stores(stores: &Self::ExternalStores) -> &GleamJsonStores {
&stores.json
}
fn component_state(state: &mut Self::RunState) -> &mut () {
&mut state.json
}
}
pub fn host_providers<Profile>() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError>
where
Profile: GleamJsonHostProfile,
{
<Component as HostProviderComponentRegistration<Profile>>::providers()
}
impl<Profile> HostProviderComponentRegistration<Profile> for Component
where
Profile: GleamJsonHostProfile,
{
fn providers() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError> {
host_provider::<Profile>().map(|provider| vec![provider])
}
}
pub(crate) fn json_stores<Profile>(stores: &Profile::ExternalStores) -> &GleamJsonStores
where
Profile: GleamJsonHostProfile,
{
<Profile as HostComponentProfile<Component>>::component_stores(stores)
}
pub(crate) fn json_state<Profile>(state: &mut Profile::RunState) -> &mut ()
where
Profile: GleamJsonHostProfile,
{
<Profile as HostComponentProfile<Component>>::component_state(state)
}
fn host_provider<Profile>() -> Result<HostProviderModule<Profile>, HostRegistrationError>
where
Profile: GleamJsonHostProfile,
{
HostProviderModule::new("gleam_json", "gleam/json")
.and_then(
HostProviderModule::with_external_type::<JsonProvider<Profile>, schema::JsonSchema>,
)
.and_then(|provider| {
provider.with_scoped_function_and_constructions::<
JsonProvider<Profile>,
(BitArrayValue,),
JsonDynamicResult,
DecodeConstructions,
_,
>("decode_to_dynamic", decode_to_dynamic::<Profile>)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (Json,), EcoString, _>(
"do_to_string",
do_to_string::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<
JsonProvider<Profile>,
(Json,),
crate::gleam_stdlib::StringTree,
_,
>("to_string_tree", to_string_tree::<Profile>)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (EcoString,), Json, _>(
"do_string",
do_string::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (bool,), Json, _>(
"do_bool",
do_bool::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (BigInt,), Json, _>(
"do_int",
do_int::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (f64,), Json, _>(
"do_float",
do_float::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (), Json, _>(
"do_null",
do_null::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (ObjectEntries,), Json, _>(
"do_object",
do_object::<Profile>,
)
})
.and_then(|provider| {
provider.with_scoped_function::<JsonProvider<Profile>, (JsonList,), Json, _>(
"do_preprocessed_array",
do_preprocessed_array::<Profile>,
)
})
}
#[cfg(test)]
mod test_support;
#[cfg(test)]
mod tests {
use super::test_support::{CustomProfile, CustomRunState, CustomStores};
use super::{
Component, GleamJsonProfile, GleamJsonProfileStores, GleamJsonRunState, host_providers,
json_stores,
};
use crate::gleam_stdlib::{Component as GleamStdlibComponent, GleamStdlibRunState};
use crate::{HostComponentProfile, HostProviderComponent, HostProviderComponentRegistration};
#[test]
fn default_and_custom_profiles_project_independent_stdlib_and_json_components() {
let default = GleamJsonProfileStores::default();
let custom = CustomStores::default();
assert!(std::ptr::eq(
<GleamJsonProfile as HostComponentProfile<GleamStdlibComponent>>::component_stores(
&default,
),
&default.stdlib,
));
assert!(std::ptr::eq(
json_stores::<GleamJsonProfile>(&default),
&default.json,
));
assert!(std::ptr::eq(
<CustomProfile as HostComponentProfile<GleamStdlibComponent>>::component_stores(
&custom,
),
&custom.stdlib,
));
assert!(std::ptr::eq(
json_stores::<CustomProfile>(&custom),
&custom.json,
));
let mut default_state = GleamJsonRunState::new(GleamStdlibRunState::from_seed([1; 32]));
let mut custom_state = CustomRunState {
stdlib: GleamStdlibRunState::from_seed([2; 32]),
json: (),
};
let default_pointer = default_state.stdlib_mut() as *mut GleamStdlibRunState;
assert!(std::ptr::eq(
<GleamJsonProfile as HostComponentProfile<GleamStdlibComponent>>::component_state(
&mut default_state,
),
default_pointer,
));
let custom_pointer = &mut custom_state.stdlib as *mut GleamStdlibRunState;
assert!(std::ptr::eq(
<CustomProfile as HostComponentProfile<GleamStdlibComponent>>::component_state(
&mut custom_state,
),
custom_pointer,
));
let default_json = &mut default_state.json as *mut ();
assert!(std::ptr::eq(
<GleamJsonProfile as HostComponentProfile<Component>>::component_state(
&mut default_state,
),
default_json,
));
let custom_json = &mut custom_state.json as *mut ();
assert!(std::ptr::eq(
<CustomProfile as HostComponentProfile<Component>>::component_state(&mut custom_state),
custom_json,
));
assert!(default_state.stdlib().io_outputs().is_empty());
assert!(custom_state.stdlib.io_outputs().is_empty());
}
#[test]
fn registers_only_the_exact_official_erlang_json_provider_inventory() {
assert_eq!(<Component as HostProviderComponent>::ID, "gleam_json");
let mut providers =
<Component as HostProviderComponentRegistration<GleamJsonProfile>>::providers()
.expect("JSON component should register");
let facade =
host_providers::<GleamJsonProfile>().expect("official JSON provider should register");
assert_eq!(facade.len(), providers.len());
assert_eq!(facade[0].module(), providers[0].module());
assert_eq!(providers.len(), 1);
let provider = providers
.pop()
.expect("JSON package should have one provider module");
assert_eq!(provider.package(), "gleam_json");
assert_eq!(provider.module(), "gleam/json");
assert_eq!(
provider
.external_types()
.map(|schema| (schema.name().as_str(), schema.parameter_count()))
.collect::<Vec<_>>(),
[("Json", 0)],
);
assert_eq!(
provider
.functions()
.map(|function| function.name().as_str())
.collect::<Vec<_>>(),
[
"decode_to_dynamic",
"do_to_string",
"to_string_tree",
"do_string",
"do_bool",
"do_int",
"do_float",
"do_null",
"do_object",
"do_preprocessed_array",
],
);
use crate::host::HostAbiType;
let json = <super::schema::Json as HostAbiType>::descriptor().value_type();
let dynamic_result =
<super::schema::JsonDynamicResult as HostAbiType>::descriptor().value_type();
let string_tree =
<crate::gleam_stdlib::StringTree as HostAbiType>::descriptor().value_type();
let object_entries =
<super::schema::ObjectEntries as HostAbiType>::descriptor().value_type();
let json_list = <super::schema::JsonList as HostAbiType>::descriptor().value_type();
let expected = [
(vec![crate::ValueType::BitArray], dynamic_result),
(vec![json.clone()], crate::ValueType::String),
(vec![json.clone()], string_tree),
(vec![crate::ValueType::String], json.clone()),
(vec![crate::ValueType::Bool], json.clone()),
(vec![crate::ValueType::Int], json.clone()),
(vec![crate::ValueType::Float], json.clone()),
(Vec::new(), json.clone()),
(vec![object_entries], json.clone()),
(vec![json_list], json),
];
for (function, (arguments, return_)) in provider.functions().zip(expected) {
assert!(function.scheme().is_monomorphic());
assert_eq!(
function.type_(),
&crate::FunctionType::new(arguments, return_),
);
}
}
}