geam-stdlib 0.2.0

Official Gleam standard-library providers for Geam
Documentation
mod function;

use super::{Component, GleamStdlibHostProfile};
use crate::{HostProviderModule, HostRegistrationError};
use ecow::EcoString;
use geam_core::provider::HostResult;
use num_bigint::BigInt;

#[geam_macros::module(
    path = "gleam/uri",
    crate_path = geam_core,
    profile = crate::GleamStdlibHostProfile,
    component = crate::Component<Profile::Io>,
)]
mod provider {
    use super::{BigInt, EcoString, HostResult, function};

    #[geam_macros::function]
    fn pop_codeunit(string: EcoString) -> (BigInt, EcoString) {
        function::pop_codeunit(string)
    }

    #[geam_macros::function]
    fn codeunit_slice(string: EcoString, from: BigInt, length: BigInt) -> HostResult<EcoString> {
        function::codeunit_slice(string, from, length).map_err(Into::into)
    }

    #[geam_macros::function]
    fn parse_query(query: EcoString) -> Result<Vec<(EcoString, EcoString)>, ()> {
        function::parse_query(query)
    }

    #[geam_macros::function]
    fn percent_encode(value: EcoString) -> EcoString {
        function::percent_encode(value)
    }

    #[geam_macros::function]
    fn percent_decode(value: EcoString) -> Result<EcoString, ()> {
        function::percent_decode(value)
    }
}

pub(super) fn host_provider<Profile>() -> Result<HostProviderModule<Profile>, HostRegistrationError>
where
    Profile: GleamStdlibHostProfile,
{
    provider::__geam_module::<Profile>()
}

#[cfg(test)]
mod tests {
    use super::host_provider;
    use crate::GleamStdlibProfile;
    use crate::ValueType;

    fn assert_result(type_: &ValueType, success: ValueType) {
        let ValueType::Custom(type_) = type_ else {
            panic!("URI result should use the prelude Result type: {type_:?}");
        };

        assert_eq!(type_.type_name().package(), "");
        assert_eq!(type_.type_name().module(), "gleam");
        assert_eq!(type_.type_name().name(), "Result");
        assert_eq!(type_.arguments(), &[success, ValueType::Nil]);
    }

    #[test]
    #[should_panic(expected = "URI result should use the prelude Result type")]
    fn result_assertion_rejects_non_result_types() {
        assert_result(&ValueType::Nil, ValueType::String);
    }

    #[test]
    fn registers_only_the_bodyless_official_uri_externals() {
        let provider =
            host_provider::<GleamStdlibProfile>().expect("official URI provider should register");

        assert_eq!(provider.package(), "gleam_stdlib");
        assert_eq!(provider.module(), "gleam/uri");
        assert_eq!(provider.external_types().count(), 0);
        let functions = provider.functions().collect::<Vec<_>>();

        assert_eq!(functions.len(), 5);
        assert_eq!(
            functions
                .iter()
                .map(|function| function.name().as_str())
                .collect::<Vec<_>>(),
            [
                "pop_codeunit",
                "codeunit_slice",
                "parse_query",
                "percent_encode",
                "percent_decode",
            ],
        );
        for function in &functions {
            assert!(function.scheme().parameters().is_empty());
        }

        assert_eq!(functions[0].type_().argument_types(), [ValueType::String]);
        assert_eq!(
            functions[0].type_().return_(),
            &ValueType::Tuple(vec![ValueType::Int, ValueType::String]),
        );
        assert_eq!(
            functions[1].type_().argument_types(),
            [ValueType::String, ValueType::Int, ValueType::Int],
        );
        assert_eq!(functions[1].type_().return_(), &ValueType::String);
        assert_eq!(functions[2].type_().argument_types(), [ValueType::String]);
        assert_result(
            functions[2].type_().return_(),
            ValueType::List(Box::new(ValueType::Tuple(vec![
                ValueType::String,
                ValueType::String,
            ]))),
        );
        assert_eq!(functions[3].type_().argument_types(), [ValueType::String]);
        assert_eq!(functions[3].type_().return_(), &ValueType::String);
        assert_eq!(functions[4].type_().argument_types(), [ValueType::String]);
        assert_result(functions[4].type_().return_(), ValueType::String);
    }
}