src/gleeam_code@internal@stdlib_extractor.erl

-module(gleeam_code@internal@stdlib_extractor).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleeam_code/internal/stdlib_extractor.gleam").
-export([list_exported/1, extract_function/2, extract_functions/2]).
-export_type([function_block/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(false).

-type function_block() :: {function_block, binary(), integer(), binary()}.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 52).
?DOC(false).
-spec collect_multiline_export(list(binary()), binary()) -> binary().
collect_multiline_export(Lines, Acc) ->
    case Lines of
        [] ->
            Acc;

        [Line | Rest] ->
            New_acc = <<<<Acc/binary, " "/utf8>>/binary,
                (gleam@string:trim(Line))/binary>>,
            case gleam_stdlib:contains_string(Line, <<"])."/utf8>>) of
                true ->
                    New_acc;

                false ->
                    collect_multiline_export(Rest, New_acc)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 35).
?DOC(false).
-spec find_export_line(list(binary()), binary()) -> binary().
find_export_line(Lines, Acc) ->
    case Lines of
        [] ->
            Acc;

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case gleam_stdlib:string_starts_with(Trimmed, <<"-export(["/utf8>>) of
                true ->
                    case gleam_stdlib:contains_string(Trimmed, <<"])."/utf8>>) of
                        true ->
                            Trimmed;

                        false ->
                            collect_multiline_export(Rest, Trimmed)
                    end;

                false ->
                    find_export_line(Rest, Acc)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 65).
?DOC(false).
-spec parse_export_entries(binary()) -> list(binary()).
parse_export_entries(Export_line) ->
    case gleam@string:split_once(Export_line, <<"["/utf8>>) of
        {error, _} ->
            [];

        {ok, {_, After_bracket}} ->
            case gleam@string:split_once(After_bracket, <<"]"/utf8>>) of
                {error, _} ->
                    [];

                {ok, {Entries_str, _}} ->
                    _pipe = Entries_str,
                    _pipe@1 = gleam@string:split(_pipe, <<","/utf8>>),
                    _pipe@5 = gleam@list:map(
                        _pipe@1,
                        fun(Entry) -> _pipe@2 = Entry,
                            _pipe@3 = gleam@string:trim(_pipe@2),
                            _pipe@4 = gleam@string:split(_pipe@3, <<"/"/utf8>>),
                            (fun(Parts) -> case Parts of
                                    [Name | _] ->
                                        gleam@string:trim(Name);

                                    _ ->
                                        <<""/utf8>>
                                end end)(_pipe@4) end
                    ),
                    gleam@list:filter(_pipe@5, fun(S) -> S /= <<""/utf8>> end)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 28).
?DOC(false).
-spec list_exported(binary()) -> list(binary()).
list_exported(Erl_source) ->
    _pipe = Erl_source,
    _pipe@1 = gleam@string:split(_pipe, <<"\n"/utf8>>),
    _pipe@2 = find_export_line(_pipe@1, <<""/utf8>>),
    parse_export_entries(_pipe@2).

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 90).
?DOC(false).
-spec find_block(list(function_block()), binary()) -> {ok, function_block()} |
    {error, nil}.
find_block(Blocks, Func_name) ->
    gleam@list:find(Blocks, fun(B) -> erlang:element(2, B) =:= Func_name end).

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 136).
?DOC(false).
-spec is_toplevel_func_start(binary()) -> boolean().
is_toplevel_func_start(Line) ->
    case gleam@string:to_graphemes(Line) of
        [] ->
            false;

        [First | _] ->
            case gleeam_code@internal@char:is_lowercase(First) of
                true ->
                    gleam_stdlib:contains_string(Line, <<"("/utf8>>);

                false ->
                    false
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 203).
?DOC(false).
-spec is_file_directive(binary()) -> boolean().
is_file_directive(Line) ->
    gleam_stdlib:string_starts_with(Line, <<"-file("/utf8>>).

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 273).
?DOC(false).
-spec is_doc_content(binary()) -> boolean().
is_doc_content(Line) ->
    ((gleam_stdlib:string_starts_with(Line, <<"\""/utf8>>) andalso gleam_stdlib:string_ends_with(
        Line,
        <<"\""/utf8>>
    ))
    orelse (Line =:= <<")."/utf8>>))
    orelse (gleam_stdlib:string_starts_with(Line, <<"    \""/utf8>>) andalso gleam_stdlib:string_ends_with(
        Line,
        <<"\""/utf8>>
    )).

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 323).
?DOC(false).
-spec count_top_level_commas(list(binary()), integer(), integer()) -> integer().
count_top_level_commas(Chars, Depth, Count) ->
    case Chars of
        [] ->
            Count;

        [C | Rest] ->
            case C of
                <<"("/utf8>> ->
                    count_top_level_commas(Rest, Depth + 1, Count);

                <<"["/utf8>> ->
                    count_top_level_commas(Rest, Depth + 1, Count);

                <<"{"/utf8>> ->
                    count_top_level_commas(Rest, Depth + 1, Count);

                <<")"/utf8>> ->
                    count_top_level_commas(Rest, Depth - 1, Count);

                <<"]"/utf8>> ->
                    count_top_level_commas(Rest, Depth - 1, Count);

                <<"}"/utf8>> ->
                    count_top_level_commas(Rest, Depth - 1, Count);

                <<","/utf8>> when Depth =:= 0 ->
                    count_top_level_commas(Rest, Depth, Count + 1);

                _ ->
                    count_top_level_commas(Rest, Depth, Count)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 310).
?DOC(false).
-spec count_args(binary()) -> integer().
count_args(Args_str) ->
    case gleam@string:split_once(Args_str, <<")"/utf8>>) of
        {error, _} ->
            0;

        {ok, {Inside, _}} ->
            Trimmed = gleam@string:trim(Inside),
            case Trimmed of
                <<""/utf8>> ->
                    0;

                _ ->
                    count_top_level_commas(
                        gleam@string:to_graphemes(Trimmed),
                        0,
                        1
                    )
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 294).
?DOC(false).
-spec parse_function_head(binary()) -> {ok, {binary(), integer()}} |
    {error, nil}.
parse_function_head(Line) ->
    case gleam@string:split_once(Line, <<"("/utf8>>) of
        {error, _} ->
            {error, nil};

        {ok, {Name, Args_rest}} ->
            Trimmed_name = gleam@string:trim(Name),
            case Trimmed_name of
                <<""/utf8>> ->
                    {error, nil};

                _ ->
                    Arity = count_args(Args_rest),
                    {ok, {Trimmed_name, Arity}}
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 279).
?DOC(false).
-spec extract_func_info(list(binary())) -> {ok, function_block()} | {error, nil}.
extract_func_info(Lines) ->
    case Lines of
        [] ->
            {error, nil};

        [First | _] ->
            case parse_function_head(gleam@string:trim(First)) of
                {ok, {Name, Arity}} ->
                    Body = gleam@string:join(Lines, <<"\n"/utf8>>),
                    {ok, {function_block, Name, Arity, Body}};

                {error, _} ->
                    {error, nil}
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 147).
?DOC(false).
-spec finalize_plain_block(list(binary())) -> {ok, function_block()} |
    {error, nil}.
finalize_plain_block(Lines) ->
    Meaningful = gleam@list:filter(
        Lines,
        fun(Line) ->
            Trimmed = gleam@string:trim(Line),
            ((((((((Trimmed /= <<""/utf8>>) andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-module("/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-compile("/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-export("/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-define("/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-if("/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-else."/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(
                Trimmed,
                <<"-endif."/utf8>>
            ))
            andalso not gleam_stdlib:string_starts_with(Trimmed, <<"%"/utf8>>)
        end
    ),
    case Meaningful of
        [] ->
            {error, nil};

        _ ->
            extract_func_info(Meaningful)
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 105).
?DOC(false).
-spec split_plain_blocks(list(binary()), list(binary()), list(function_block())) -> list(function_block()).
split_plain_blocks(Lines, Current_block, Acc) ->
    case Lines of
        [] ->
            case Current_block of
                [] ->
                    lists:reverse(Acc);

                _ ->
                    case finalize_plain_block(lists:reverse(Current_block)) of
                        {ok, Block} ->
                            lists:reverse([Block | Acc]);

                        {error, _} ->
                            lists:reverse(Acc)
                    end
            end;

        [Line | Rest] ->
            case is_toplevel_func_start(Line) andalso (Current_block /= []) of
                true ->
                    New_acc = case finalize_plain_block(
                        lists:reverse(Current_block)
                    ) of
                        {ok, Block@1} ->
                            [Block@1 | Acc];

                        {error, _} ->
                            Acc
                    end,
                    split_plain_blocks(Rest, [Line], New_acc);

                false ->
                    split_plain_blocks(Rest, [Line | Current_block], Acc)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 240).
?DOC(false).
-spec skip_spec_block(binary(), list(binary())) -> list(binary()).
skip_spec_block(First_line, Rest) ->
    case gleam_stdlib:string_ends_with(First_line, <<"."/utf8>>) of
        true ->
            skip_non_code(Rest);

        false ->
            skip_until_spec_end(Rest)
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 247).
?DOC(false).
-spec skip_until_spec_end(list(binary())) -> list(binary()).
skip_until_spec_end(Lines) ->
    case Lines of
        [] ->
            [];

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case gleam_stdlib:string_ends_with(Trimmed, <<"."/utf8>>) of
                true ->
                    skip_non_code(Rest);

                false ->
                    skip_until_spec_end(Rest)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 215).
?DOC(false).
-spec skip_non_code(list(binary())) -> list(binary()).
skip_non_code(Lines) ->
    case Lines of
        [] ->
            [];

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case is_file_directive(Trimmed) of
                true ->
                    skip_non_code(Rest);

                false ->
                    case gleam_stdlib:string_starts_with(
                        Trimmed,
                        <<"?DOC("/utf8>>
                    ) of
                        true ->
                            skip_doc_block(Rest);

                        false ->
                            case gleam_stdlib:string_starts_with(
                                Trimmed,
                                <<"-spec "/utf8>>
                            ) of
                                true ->
                                    skip_spec_block(Trimmed, Rest);

                                false ->
                                    case is_doc_content(Trimmed) of
                                        true ->
                                            skip_non_code(Rest);

                                        false ->
                                            [Line | Rest]
                                    end
                            end
                    end
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 207).
?DOC(false).
-spec finalize_block(list(binary())) -> {ok, function_block()} | {error, nil}.
finalize_block(Lines) ->
    Meaningful = skip_non_code(Lines),
    case Meaningful of
        [] ->
            {error, nil};

        _ ->
            extract_func_info(Meaningful)
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 167).
?DOC(false).
-spec split_into_blocks(list(binary()), list(binary()), list(function_block())) -> list(function_block()).
split_into_blocks(Lines, Current_block, Acc) ->
    case Lines of
        [] ->
            case Current_block of
                [] ->
                    lists:reverse(Acc);

                _ ->
                    case finalize_block(lists:reverse(Current_block)) of
                        {ok, Block} ->
                            lists:reverse([Block | Acc]);

                        {error, _} ->
                            lists:reverse(Acc)
                    end
            end;

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case is_file_directive(Trimmed) of
                true ->
                    New_acc = case Current_block of
                        [] ->
                            Acc;

                        _ ->
                            case finalize_block(lists:reverse(Current_block)) of
                                {ok, Block@1} ->
                                    [Block@1 | Acc];

                                {error, _} ->
                                    Acc
                            end
                    end,
                    split_into_blocks(Rest, [Line], New_acc);

                false ->
                    split_into_blocks(Rest, [Line | Current_block], Acc)
            end
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 97).
?DOC(false).
-spec parse_blocks(binary()) -> list(function_block()).
parse_blocks(Erl_source) ->
    Lines = gleam@string:split(Erl_source, <<"\n"/utf8>>),
    case gleam_stdlib:contains_string(Erl_source, <<"-file("/utf8>>) of
        true ->
            split_into_blocks(Lines, [], []);

        false ->
            split_plain_blocks(Lines, [], [])
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 9).
?DOC(false).
-spec extract_function(binary(), binary()) -> {ok, binary()} | {error, nil}.
extract_function(Erl_source, Func_name) ->
    Blocks = parse_blocks(Erl_source),
    case find_block(Blocks, Func_name) of
        {ok, Block} ->
            {ok, erlang:element(4, Block)};

        {error, _} ->
            {error, nil}
    end.

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 20).
?DOC(false).
-spec extract_functions(binary(), list(binary())) -> binary().
extract_functions(Erl_source, Func_names) ->
    Blocks = parse_blocks(Erl_source),
    _pipe = Func_names,
    _pipe@1 = gleam@list:filter_map(
        _pipe,
        fun(Name) -> find_block(Blocks, Name) end
    ),
    _pipe@2 = gleam@list:map(_pipe@1, fun(B) -> erlang:element(4, B) end),
    gleam@string:join(_pipe@2, <<"\n\n"/utf8>>).

-file("src/gleeam_code/internal/stdlib_extractor.gleam", 260).
?DOC(false).
-spec skip_doc_block(list(binary())) -> list(binary()).
skip_doc_block(Lines) ->
    case Lines of
        [] ->
            [];

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case Trimmed =:= <<")."/utf8>> of
                true ->
                    skip_non_code(Rest);

                false ->
                    skip_doc_block(Rest)
            end
    end.