geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
use super::DynamicDecodeProvider;
use crate::gleam_stdlib::GleamStdlibHostProfile;
use crate::gleam_stdlib::dynamic::{self, Dynamic, DynamicSequence};
use crate::gleam_stdlib::dynamic_decode::schema::{
    DecodeErrorConstructor, DecodeListConstructions, DecodeListErrorIndex, DecodeListErrorsIndex,
    DecodeListPathIndex, DecodeListResult, DecodeListValuesIndex, DecodedItem, DictError, DictOk,
    DictResult, DynamicDict, ItemDecodeLayer, ItemDecoderArguments, PushPath,
};
use crate::{
    HostCall, HostCallCompletion, HostCallError, HostCallable, HostConstructions, HostExternal,
    HostList, HostValue,
};
use ecow::EcoString;
use num_bigint::BigInt;

pub(in crate::gleam_stdlib::dynamic_decode) fn decode_list<'call, Profile>(
    mut call: HostCall<'call, Profile, DynamicDecodeProvider<Profile>, DecodeListResult>,
    constructions: HostConstructions<'call, DecodeListConstructions>,
    data: HostExternal<'call, Dynamic>,
    item: HostCallable<'call, ItemDecoderArguments, ItemDecodeLayer>,
    _push_path: HostValue<'call, PushPath>,
    mut index: BigInt,
    accumulator: HostList<'call, DecodedItem>,
) -> Result<HostCallCompletion<'call, DecodeListResult>, HostCallError>
where
    Profile: GleamStdlibHostProfile,
{
    let Some(sequence) = dynamic::sequence(&mut call, data) else {
        let found = dynamic::classification(&call, data);
        let path = call.construct_list(constructions.at::<DecodeListPathIndex>(), []);
        let error = call.construct_custom::<DecodeErrorConstructor>(
            constructions.at::<DecodeListErrorIndex>(),
            (EcoString::from("List"), (found, (path, ()))),
        );
        let values = call.construct_list(constructions.at::<DecodeListValuesIndex>(), []);
        let errors = call.construct_list(constructions.at::<DecodeListErrorsIndex>(), [error]);
        return Ok(call.return_tuple((values, (errors, ()))));
    };

    let values = match sequence {
        DynamicSequence::List(values) | DynamicSequence::Array(values) => values,
    };
    let mut decoded = Vec::<HostValue<'call, DecodedItem>>::new();
    let mut accumulator_index = 0;
    while let Some(value) = call.list_item(accumulator, accumulator_index) {
        decoded.push(value);
        accumulator_index += 1;
    }
    decoded.reverse();

    let mut value_index = 0;
    while let Some(value) = call.list_item(values, value_index) {
        let layer = call.invoke(item, (value, ()))?;
        let (value, (errors, ())) = call.tuple_values(layer);
        if call.list_len(errors) != 0 {
            let mut updated_errors = Vec::new();
            let mut error_index = 0;
            while let Some(error) = call.list_item(errors, error_index) {
                let (expected, (found, (path, ()))) =
                    call.sole_custom_fields::<DecodeErrorConstructor>(error);
                let mut updated_path = vec![EcoString::from(index.to_string())];
                let mut path_index = 0;
                while let Some(segment) = call.list_item(path, path_index) {
                    updated_path.push(segment);
                    path_index += 1;
                }
                let path =
                    call.construct_list(constructions.at::<DecodeListPathIndex>(), updated_path);
                updated_errors.push(call.construct_custom::<DecodeErrorConstructor>(
                    constructions.at::<DecodeListErrorIndex>(),
                    (expected, (found, (path, ()))),
                ));
                error_index += 1;
            }
            let empty = call.construct_list(constructions.at::<DecodeListValuesIndex>(), []);
            let errors =
                call.construct_list(constructions.at::<DecodeListErrorsIndex>(), updated_errors);
            return Ok(call.return_tuple((empty, (errors, ()))));
        }
        decoded.push(value);
        value_index += 1;
        index += 1;
    }

    let values = call.construct_list(constructions.at::<DecodeListValuesIndex>(), decoded);
    let errors = call.construct_list(constructions.at::<DecodeListErrorsIndex>(), []);
    Ok(call.return_tuple((values, (errors, ()))))
}

pub(in crate::gleam_stdlib::dynamic_decode) fn decode_dict<'call, Profile>(
    mut call: HostCall<'call, Profile, DynamicDecodeProvider<Profile>, DictResult>,
    value: HostExternal<'call, Dynamic>,
) -> Result<HostCallCompletion<'call, DictResult>, HostCallError>
where
    Profile: GleamStdlibHostProfile,
{
    match dynamic::decode_value::<_, _, _, DynamicDict>(&mut call, value) {
        Some(value) => Ok(call.return_custom::<DictOk>((value, ()))),
        None => Ok(call.return_custom::<DictError>(((), ()))),
    }
}