Skip to main content

src/gleamdoc@index.erl

-module(gleamdoc@index).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleamdoc/index.gleam").
-export([package_interface_paths/3, source_location/4]).
-export_type([section/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(" Local package-interface indexing and source-location discovery.\n").

-type section() :: normal | dependencies | dev_dependencies.

-file("src/gleamdoc/index.gleam", 870).
-spec file_result({ok, JIZ} | {error, simplifile:file_error()}, binary()) -> {ok,
        JIZ} |
    {error, binary()}.
file_result(Result, Context) ->
    _pipe = Result,
    gleam@result:map_error(
        _pipe,
        fun(Error) ->
            <<<<Context/binary, ": "/utf8>>/binary,
                (simplifile:describe_error(Error))/binary>>
        end
    ).

-file("src/gleamdoc/index.gleam", 884).
-spec join3(binary(), binary(), binary()) -> binary().
join3(First, Second, Third) ->
    filepath:join(filepath:join(First, Second), Third).

-file("src/gleamdoc/index.gleam", 888).
-spec join4(binary(), binary(), binary(), binary()) -> binary().
join4(First, Second, Third, Fourth) ->
    _pipe = join3(First, Second, Third),
    filepath:join(_pipe, Fourth).

-file("src/gleamdoc/index.gleam", 285).
-spec cleanup_staging(binary()) -> {ok, nil} | {error, binary()}.
cleanup_staging(Root) ->
    Staging = join4(
        Root,
        <<"build"/utf8>>,
        <<"gleamdoc"/utf8>>,
        <<"staging"/utf8>>
    ),
    _pipe = simplifile:delete_all([Staging]),
    file_result(
        _pipe,
        <<"Could not remove staging directory "/utf8, Staging/binary>>
    ).

-file("src/gleamdoc/index.gleam", 544).
-spec fingerprint_path(binary()) -> binary().
fingerprint_path(Output) ->
    <<Output/binary, ".fingerprint"/utf8>>.

-file("src/gleamdoc/index.gleam", 519).
-spec write_fingerprint(binary(), list(binary())) -> {ok, nil} |
    {error, binary()}.
write_fingerprint(Output, Files) ->
    gleam@result:'try'(
        gleamdoc_ffi:interface_fingerprint(Files),
        fun(Fingerprint) ->
            _pipe = simplifile:write(fingerprint_path(Output), Fingerprint),
            file_result(
                _pipe,
                <<"Could not write cache fingerprint for "/utf8, Output/binary>>
            )
        end
    ).

-file("src/gleamdoc/index.gleam", 880).
-spec file_error(binary(), simplifile:file_error()) -> binary().
file_error(Path, Error) ->
    <<<<<<"Could not read "/utf8, Path/binary>>/binary, ": "/utf8>>/binary,
        (simplifile:describe_error(Error))/binary>>.

-file("src/gleamdoc/index.gleam", 509).
-spec matching_files(binary(), fun((binary()) -> boolean())) -> {ok,
        list(binary())} |
    {error, binary()}.
matching_files(Directory, Matches) ->
    case simplifile:get_files(Directory) of
        {error, Error} ->
            {error, file_error(Directory, Error)};

        {ok, Paths} ->
            {ok, gleam@list:filter(Paths, Matches)}
    end.

-file("src/gleamdoc/index.gleam", 498).
-spec dependency_interface_files(binary()) -> {ok, list(binary())} |
    {error, binary()}.
dependency_interface_files(Directory) ->
    matching_files(Directory, fun(Path) -> case filepath:extension(Path) of
                {ok, <<"gleam"/utf8>>} ->
                    true;

                {ok, <<"toml"/utf8>>} ->
                    true;

                _ ->
                    false
            end end).

-file("src/gleamdoc/index.gleam", 476).
-spec write_dependency_fingerprint(binary(), binary()) -> {ok, nil} |
    {error, binary()}.
write_dependency_fingerprint(Package_directory, Output) ->
    gleam@result:'try'(
        dependency_interface_files(Package_directory),
        fun(Files) -> write_fingerprint(Output, Files) end
    ).

-file("src/gleamdoc/index.gleam", 442).
-spec create_symlink(binary(), binary()) -> {ok, nil} | {error, binary()}.
create_symlink(Target, Link) ->
    _pipe = simplifile_erl:create_symlink(Target, Link),
    file_result(_pipe, <<"Could not create symlink "/utf8, Link/binary>>).

-file("src/gleamdoc/index.gleam", 429).
-spec link_optional_directory(binary(), binary(), binary()) -> {ok, nil} |
    {error, binary()}.
link_optional_directory(Package_directory, Stage, Name) ->
    Source = filepath:join(Package_directory, Name),
    case simplifile_erl:is_directory(Source) of
        {ok, true} ->
            create_symlink(Source, filepath:join(Stage, Name));

        {ok, false} ->
            {ok, nil};

        {error, enoent} ->
            {ok, nil};

        {error, Error} ->
            {error, file_error(Source, Error)}
    end.

-file("src/gleamdoc/index.gleam", 403).
-spec remove_requirements_section(list(binary()), list(binary()), boolean()) -> list(binary()).
remove_requirements_section(Lines, Output, Keeping) ->
    case Lines of
        [] ->
            Output;

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case {Trimmed =:= <<"[requirements]"/utf8>>,
                Keeping,
                gleam_stdlib:string_starts_with(Trimmed, <<"["/utf8>>)} of
                {true, _, _} ->
                    remove_requirements_section(Rest, Output, false);

                {false, false, true} ->
                    remove_requirements_section(Rest, [Line | Output], true);

                {false, false, false} ->
                    remove_requirements_section(Rest, Output, false);

                {false, true, _} ->
                    remove_requirements_section(Rest, [Line | Output], true)
            end
    end.

-file("src/gleamdoc/index.gleam", 374).
-spec prepare_manifest(binary(), binary(), list({binary(), binary()})) -> {ok,
        nil} |
    {error, binary()}.
prepare_manifest(Source, Destination, Requirements) ->
    gleam@result:'try'(
        begin
            _pipe = simplifile:read(Source),
            file_result(_pipe, <<"Could not read "/utf8, Source/binary>>)
        end,
        fun(Manifest) ->
            Packages = begin
                _pipe@1 = Manifest,
                _pipe@2 = gleam@string:split(_pipe@1, <<"\n"/utf8>>),
                _pipe@3 = remove_requirements_section(_pipe@2, [], true),
                _pipe@4 = lists:reverse(_pipe@3),
                _pipe@5 = gleam@string:join(_pipe@4, <<"\n"/utf8>>),
                gleam@string:trim_end(_pipe@5)
            end,
            Requirement_lines = begin
                _pipe@6 = Requirements,
                _pipe@7 = gleam@list:map(
                    _pipe@6,
                    fun(Requirement) ->
                        {Name, Constraint} = Requirement,
                        <<<<<<Name/binary, " = { version = "/utf8>>/binary,
                                Constraint/binary>>/binary,
                            " }"/utf8>>
                    end
                ),
                gleam@string:join(_pipe@7, <<"\n"/utf8>>)
            end,
            _pipe@8 = simplifile:write(
                Destination,
                <<<<<<Packages/binary, "\n\n[requirements]\n"/utf8>>/binary,
                        Requirement_lines/binary>>/binary,
                    "\n"/utf8>>
            ),
            file_result(
                _pipe@8,
                <<"Could not write "/utf8, Destination/binary>>
            )
        end
    ).

-file("src/gleamdoc/index.gleam", 355).
-spec dependency_requirement(binary()) -> gleam@option:option({binary(),
    binary()}).
dependency_requirement(Line) ->
    String_pattern@1 = case gleam@regexp:from_string(
        <<"^\\s*([A-Za-z0-9_]+)\\s*=\\s*(\"[^\"]+\")"/utf8>>
    ) of
        {ok, String_pattern} -> String_pattern;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"gleamdoc/index"/utf8>>,
                        function => <<"dependency_requirement"/utf8>>,
                        line => 356,
                        value => _assert_fail,
                        start => 10611,
                        'end' => 10710,
                        pattern_start => 10622,
                        pattern_end => 10640})
    end,
    Table_pattern@1 = case gleam@regexp:from_string(
        <<"^\\s*([A-Za-z0-9_]+)\\s*=\\s*\\{.*version\\s*=\\s*(\"[^\"]+\")"/utf8>>
    ) of
        {ok, Table_pattern} -> Table_pattern;
        _assert_fail@1 ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"gleamdoc/index"/utf8>>,
                        function => <<"dependency_requirement"/utf8>>,
                        line => 358,
                        value => _assert_fail@1,
                        start => 10713,
                        'end' => 10845,
                        pattern_start => 10724,
                        pattern_end => 10741})
    end,
    case gleam@regexp:scan(String_pattern@1, Line) of
        [{match, _, [{some, Name}, {some, Constraint}]} | _] ->
            {some, {Name, Constraint}};

        _ ->
            case gleam@regexp:scan(Table_pattern@1, Line) of
                [{match, _, [{some, Name@1}, {some, Constraint@1}]} | _] ->
                    {some, {Name@1, Constraint@1}};

                _ ->
                    none
            end
    end.

-file("src/gleamdoc/index.gleam", 310).
-spec filter_config(
    list(binary()),
    section(),
    list(binary()),
    list({binary(), binary()})
) -> {list(binary()), list({binary(), binary()})}.
filter_config(Lines, Section, Output, Requirements) ->
    case Lines of
        [] ->
            {Output, Requirements};

        [Line | Rest] ->
            Trimmed = gleam@string:trim(Line),
            case {Trimmed, Section} of
                {<<"[dependencies]"/utf8>>, _} ->
                    filter_config(
                        Rest,
                        dependencies,
                        [Line | Output],
                        Requirements
                    );

                {<<"[dev-dependencies]"/utf8>>, _} ->
                    filter_config(Rest, dev_dependencies, Output, Requirements);

                {<<"[dev_dependencies]"/utf8>>, _} ->
                    filter_config(Rest, dev_dependencies, Output, Requirements);

                {_, dev_dependencies} ->
                    case gleam_stdlib:string_starts_with(Trimmed, <<"["/utf8>>) of
                        true ->
                            filter_config(
                                Rest,
                                normal,
                                [Line | Output],
                                Requirements
                            );

                        false ->
                            filter_config(
                                Rest,
                                dev_dependencies,
                                Output,
                                Requirements
                            )
                    end;

                {_, dependencies} ->
                    case gleam_stdlib:string_starts_with(Trimmed, <<"["/utf8>>) of
                        true ->
                            filter_config(
                                Rest,
                                normal,
                                [Line | Output],
                                Requirements
                            );

                        false ->
                            case dependency_requirement(Line) of
                                {some, Requirement} ->
                                    filter_config(
                                        Rest,
                                        dependencies,
                                        [Line | Output],
                                        [Requirement | Requirements]
                                    );

                                none ->
                                    filter_config(
                                        Rest,
                                        dependencies,
                                        [Line | Output],
                                        Requirements
                                    )
                            end
                    end;

                {_, normal} ->
                    filter_config(Rest, normal, [Line | Output], Requirements)
            end
    end.

-file("src/gleamdoc/index.gleam", 291).
-spec prepare_config(binary(), binary()) -> {ok, list({binary(), binary()})} |
    {error, binary()}.
prepare_config(Source, Destination) ->
    gleam@result:'try'(
        begin
            _pipe = simplifile:read(Source),
            file_result(_pipe, <<"Could not read "/utf8, Source/binary>>)
        end,
        fun(Config) ->
            {Lines, Requirements} = filter_config(
                gleam@string:split(Config, <<"\n"/utf8>>),
                normal,
                [],
                []
            ),
            gleam@result:'try'(
                begin
                    _pipe@3 = simplifile:write(
                        Destination,
                        begin
                            _pipe@1 = Lines,
                            _pipe@2 = lists:reverse(_pipe@1),
                            gleam@string:join(_pipe@2, <<"\n"/utf8>>)
                        end
                    ),
                    file_result(
                        _pipe@3,
                        <<"Could not write "/utf8, Destination/binary>>
                    )
                end,
                fun(_) -> {ok, lists:reverse(Requirements)} end
            )
        end
    ).

-file("src/gleamdoc/index.gleam", 447).
-spec ensure_directory(binary()) -> {ok, nil} | {error, binary()}.
ensure_directory(Path) ->
    _pipe = simplifile:create_directory_all(Path),
    file_result(_pipe, <<"Could not create directory "/utf8, Path/binary>>).

-file("src/gleamdoc/index.gleam", 245).
-spec prepare_stage(binary(), binary(), binary()) -> {ok, binary()} |
    {error, binary()}.
prepare_stage(Root, Package_directory, Name) ->
    Stage = begin
        _pipe = join4(
            Root,
            <<"build"/utf8>>,
            <<"gleamdoc"/utf8>>,
            <<"staging"/utf8>>
        ),
        filepath:join(_pipe, Name)
    end,
    Build_directory = filepath:join(Stage, <<"build"/utf8>>),
    gleam@result:'try'(
        begin
            _pipe@1 = simplifile:delete_all([Stage]),
            file_result(
                _pipe@1,
                <<"Could not reset staging directory "/utf8, Stage/binary>>
            )
        end,
        fun(_) ->
            gleam@result:'try'(
                ensure_directory(Build_directory),
                fun(_) ->
                    gleam@result:'try'(
                        prepare_config(
                            filepath:join(
                                Package_directory,
                                <<"gleam.toml"/utf8>>
                            ),
                            filepath:join(Stage, <<"gleam.toml"/utf8>>)
                        ),
                        fun(Requirements) ->
                            gleam@result:'try'(
                                prepare_manifest(
                                    filepath:join(
                                        Root,
                                        <<"manifest.toml"/utf8>>
                                    ),
                                    filepath:join(
                                        Stage,
                                        <<"manifest.toml"/utf8>>
                                    ),
                                    Requirements
                                ),
                                fun(_) ->
                                    gleam@result:'try'(
                                        create_symlink(
                                            filepath:join(
                                                Package_directory,
                                                <<"src"/utf8>>
                                            ),
                                            filepath:join(Stage, <<"src"/utf8>>)
                                        ),
                                        fun(_) ->
                                            gleam@result:'try'(
                                                create_symlink(
                                                    join3(
                                                        Root,
                                                        <<"build"/utf8>>,
                                                        <<"packages"/utf8>>
                                                    ),
                                                    filepath:join(
                                                        Build_directory,
                                                        <<"packages"/utf8>>
                                                    )
                                                ),
                                                fun(_) ->
                                                    gleam@result:'try'(
                                                        link_optional_directory(
                                                            Package_directory,
                                                            Stage,
                                                            <<"include"/utf8>>
                                                        ),
                                                        fun(_) ->
                                                            gleam@result:'try'(
                                                                link_optional_directory(
                                                                    Package_directory,
                                                                    Stage,
                                                                    <<"priv"/utf8>>
                                                                ),
                                                                fun(_) ->
                                                                    {ok, Stage}
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/gleamdoc/index.gleam", 548).
-spec stale_cache_message(binary()) -> binary().
stale_cache_message(Package) ->
    <<<<"The package interface cache for "/utf8, Package/binary>>/binary,
        " is missing or stale. Run `gleamdoc index` to refresh it."/utf8>>.

-file("src/gleamdoc/index.gleam", 528).
-spec cache_matches_fingerprint(binary(), list(binary())) -> {ok, boolean()} |
    {error, binary()}.
cache_matches_fingerprint(Output, Files) ->
    case {simplifile_erl:is_file(Output),
        simplifile:read(fingerprint_path(Output))} of
        {{ok, true}, {ok, Stored}} ->
            gleam@result:'try'(
                gleamdoc_ffi:interface_fingerprint(Files),
                fun(Current) -> {ok, Stored =:= Current} end
            );

        {{ok, false}, _} ->
            {ok, false};

        {{error, enoent}, _} ->
            {ok, false};

        {_, {error, enoent}} ->
            {ok, false};

        {{error, Error}, _} ->
            {error, file_error(Output, Error)};

        {_, {error, Error@1}} ->
            {error, file_error(fingerprint_path(Output), Error@1)}
    end.

-file("src/gleamdoc/index.gleam", 460).
-spec cache_is_fresh(binary(), binary()) -> {ok, boolean()} | {error, binary()}.
cache_is_fresh(Output, Package_directory) ->
    gleam@result:'try'(
        dependency_interface_files(Package_directory),
        fun(Files) -> cache_matches_fingerprint(Output, Files) end
    ).

-file("src/gleamdoc/index.gleam", 160).
-spec export_dependencies(
    binary(),
    binary(),
    binary(),
    list(binary()),
    boolean(),
    list(binary())
) -> {ok, list(binary())} | {error, binary()}.
export_dependencies(Root, Packages_directory, Cache, Names, Refresh, Outputs) ->
    case Names of
        [] ->
            {ok, lists:reverse(Outputs)};

        [Name | Rest] ->
            Package_directory = filepath:join(Packages_directory, Name),
            Config = filepath:join(Package_directory, <<"gleam.toml"/utf8>>),
            Output = filepath:join(Cache, <<Name/binary, ".json"/utf8>>),
            case simplifile_erl:is_file(Config) of
                {error, enoent} ->
                    export_dependencies(
                        Root,
                        Packages_directory,
                        Cache,
                        Rest,
                        Refresh,
                        Outputs
                    );

                {error, enotdir} ->
                    export_dependencies(
                        Root,
                        Packages_directory,
                        Cache,
                        Rest,
                        Refresh,
                        Outputs
                    );

                {error, Error} ->
                    {error, file_error(Config, Error)};

                {ok, false} ->
                    export_dependencies(
                        Root,
                        Packages_directory,
                        Cache,
                        Rest,
                        Refresh,
                        Outputs
                    );

                {ok, true} ->
                    gleam@result:'try'(
                        ensure_directory(Cache),
                        fun(_) ->
                            gleam@result:'try'(
                                cache_is_fresh(Output, Package_directory),
                                fun(Fresh) -> case Fresh of
                                        true ->
                                            export_dependencies(
                                                Root,
                                                Packages_directory,
                                                Cache,
                                                Rest,
                                                Refresh,
                                                [Output | Outputs]
                                            );

                                        false when not Refresh ->
                                            {error, stale_cache_message(Name)};

                                        false ->
                                            gleam@result:'try'(
                                                prepare_stage(
                                                    Root,
                                                    Package_directory,
                                                    Name
                                                ),
                                                fun(Stage) ->
                                                    Stage_output = filepath:join(
                                                        Stage,
                                                        <<"package-interface.json"/utf8>>
                                                    ),
                                                    gleam@result:'try'(
                                                        begin
                                                            _pipe = gleamdoc_ffi:export_package_interface(
                                                                Stage,
                                                                Stage_output
                                                            ),
                                                            gleam@result:map_error(
                                                                _pipe,
                                                                fun(Message) ->
                                                                    <<<<<<"Failed to index "/utf8,
                                                                                Name/binary>>/binary,
                                                                            ":\n"/utf8>>/binary,
                                                                        Message/binary>>
                                                                end
                                                            )
                                                        end,
                                                        fun(_) ->
                                                            gleam@result:'try'(
                                                                begin
                                                                    _pipe@1 = simplifile:copy_file(
                                                                        Stage_output,
                                                                        Output
                                                                    ),
                                                                    file_result(
                                                                        _pipe@1,
                                                                        <<"Could not copy "/utf8,
                                                                            Stage_output/binary>>
                                                                    )
                                                                end,
                                                                fun(_) ->
                                                                    gleam@result:'try'(
                                                                        write_dependency_fingerprint(
                                                                            Package_directory,
                                                                            Output
                                                                        ),
                                                                        fun(_) ->
                                                                            export_dependencies(
                                                                                Root,
                                                                                Packages_directory,
                                                                                Cache,
                                                                                Rest,
                                                                                Refresh,
                                                                                [Output |
                                                                                    Outputs]
                                                                            )
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                    end end
                            )
                        end
                    )
            end
    end.

-file("src/gleamdoc/index.gleam", 148).
-spec package_name(binary()) -> gleam@option:option(binary()).
package_name(Config) ->
    Pattern@1 = case gleam@regexp:compile(
        <<"^name\\s*=\\s*\"([^\"]+)\""/utf8>>,
        {options, false, true}
    ) of
        {ok, Pattern} -> Pattern;
        _assert_fail ->
            erlang:error(#{gleam_error => let_assert,
                        message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                        file => <<?FILEPATH/utf8>>,
                        module => <<"gleamdoc/index"/utf8>>,
                        function => <<"package_name"/utf8>>,
                        line => 149,
                        value => _assert_fail,
                        start => 4145,
                        'end' => 4302,
                        pattern_start => 4156,
                        pattern_end => 4167})
    end,
    case gleam@regexp:scan(Pattern@1, Config) of
        [{match, _, [{some, Name}]} | _] ->
            {some, Name};

        _ ->
            none
    end.

-file("src/gleamdoc/index.gleam", 140).
-spec is_current_package(binary(), binary()) -> {ok, boolean()} |
    {error, binary()}.
is_current_package(Root, Package) ->
    Config = filepath:join(Root, <<"gleam.toml"/utf8>>),
    case simplifile:read(Config) of
        {error, Error} ->
            {error, file_error(Config, Error)};

        {ok, Content} ->
            {ok, package_name(Content) =:= {some, Package}}
    end.

-file("src/gleamdoc/index.gleam", 133).
-spec select_packages(list(binary()), binary()) -> list(binary()).
select_packages(Names, Package) ->
    case Package of
        <<""/utf8>> ->
            Names;

        _ ->
            gleam@list:filter(Names, fun(Name) -> Name =:= Package end)
    end.

-file("src/gleamdoc/index.gleam", 63).
-spec dependency_interface_paths(
    binary(),
    binary(),
    binary(),
    boolean(),
    list(binary())
) -> {ok, list(binary())} | {error, binary()}.
dependency_interface_paths(Root, Cache, Package, Refresh, Outputs) ->
    Packages_directory = join3(Root, <<"build"/utf8>>, <<"packages"/utf8>>),
    Indexing_result = case simplifile_erl:read_directory(Packages_directory) of
        {error, enoent} ->
            gleam@result:'try'(
                is_current_package(Root, Package),
                fun(Current_package) -> case {Package, Current_package} of
                        {<<""/utf8>>, _} ->
                            {ok, lists:reverse(Outputs)};

                        {_, true} ->
                            {ok, lists:reverse(Outputs)};

                        {_, false} ->
                            {error,
                                <<<<"Package `"/utf8, Package/binary>>/binary,
                                    "` is not present in build/packages."/utf8>>}
                    end end
            );

        {error, Error} ->
            {error, file_error(Packages_directory, Error)};

        {ok, Names} ->
            Selected = select_packages(Names, Package),
            case {Package, Selected} of
                {<<""/utf8>>, _} ->
                    export_dependencies(
                        Root,
                        Packages_directory,
                        filepath:join(Cache, <<"packages"/utf8>>),
                        gleam@list:sort(Selected, fun gleam@string:compare/2),
                        Refresh,
                        Outputs
                    );

                {_, []} ->
                    gleam@result:'try'(
                        is_current_package(Root, Package),
                        fun(Current_package@1) -> case Current_package@1 of
                                true ->
                                    {ok, lists:reverse(Outputs)};

                                false ->
                                    {error,
                                        <<<<"Package `"/utf8, Package/binary>>/binary,
                                            "` is not present in build/packages."/utf8>>}
                            end end
                    );

                {_, _} ->
                    export_dependencies(
                        Root,
                        Packages_directory,
                        filepath:join(Cache, <<"packages"/utf8>>),
                        Selected,
                        Refresh,
                        Outputs
                    )
            end
    end,
    case {Refresh, Indexing_result} of
        {false, _} ->
            Indexing_result;

        {true, {error, Message}} ->
            case cleanup_staging(Root) of
                {ok, nil} ->
                    {error, Message};

                {error, Cleanup_message} ->
                    {error,
                        <<<<Message/binary,
                                "\nAdditionally, cleanup failed: "/utf8>>/binary,
                            Cleanup_message/binary>>}
            end;

        {true, {ok, Paths}} ->
            case cleanup_staging(Root) of
                {ok, nil} ->
                    {ok, Paths};

                {error, Message@1} ->
                    {error, Message@1}
            end
    end.

-file("src/gleamdoc/index.gleam", 484).
-spec current_interface_files(binary()) -> {ok, list(binary())} |
    {error, binary()}.
current_interface_files(Root) ->
    Source_directory = filepath:join(Root, <<"src"/utf8>>),
    gleam@result:'try'(
        matching_files(
            Source_directory,
            fun(Path) -> filepath:extension(Path) =:= {ok, <<"gleam"/utf8>>} end
        ),
        fun(Sources) ->
            {ok,
                [filepath:join(Root, <<"gleam.toml"/utf8>>),
                    filepath:join(Root, <<"manifest.toml"/utf8>>) |
                    Sources]}
        end
    ).

-file("src/gleamdoc/index.gleam", 452).
-spec current_cache_is_fresh(binary(), binary()) -> {ok, boolean()} |
    {error, binary()}.
current_cache_is_fresh(Output, Root) ->
    gleam@result:'try'(
        current_interface_files(Root),
        fun(Files) -> cache_matches_fingerprint(Output, Files) end
    ).

-file("src/gleamdoc/index.gleam", 468).
-spec write_current_fingerprint(binary(), binary()) -> {ok, nil} |
    {error, binary()}.
write_current_fingerprint(Root, Output) ->
    gleam@result:'try'(
        current_interface_files(Root),
        fun(Files) -> write_fingerprint(Output, Files) end
    ).

-file("src/gleamdoc/index.gleam", 43).
-spec prepare_current_interface(binary(), binary(), boolean()) -> {ok, nil} |
    {error, binary()}.
prepare_current_interface(Root, Output, Refresh) ->
    case Refresh of
        true ->
            gleam@result:'try'(
                gleamdoc_ffi:export_package_interface(Root, Output),
                fun(_) -> write_current_fingerprint(Root, Output) end
            );

        false ->
            gleam@result:'try'(
                current_cache_is_fresh(Output, Root),
                fun(Fresh) -> case Fresh of
                        true ->
                            {ok, nil};

                        false ->
                            {error,
                                stale_cache_message(<<"current project"/utf8>>)}
                    end end
            )
    end.

-file("src/gleamdoc/index.gleam", 22).
?DOC(" Returns package-interface cache paths, refreshing them when requested.\n").
-spec package_interface_paths(boolean(), binary(), boolean()) -> {ok,
        list(binary())} |
    {error, binary()}.
package_interface_paths(Dependencies, Package, Refresh) ->
    gleam@result:'try'(
        begin
            _pipe = simplifile:current_directory(),
            file_result(
                _pipe,
                <<"Could not determine the current directory"/utf8>>
            )
        end,
        fun(Root) ->
            Cache = join3(Root, <<"build"/utf8>>, <<"gleamdoc"/utf8>>),
            Current_output = filepath:join(
                Cache,
                <<"package-interface.json"/utf8>>
            ),
            gleam@result:'try'(
                ensure_directory(Cache),
                fun(_) ->
                    gleam@result:'try'(
                        prepare_current_interface(Root, Current_output, Refresh),
                        fun(_) -> case Dependencies of
                                false ->
                                    {ok, [Current_output]};

                                true ->
                                    dependency_interface_paths(
                                        Root,
                                        Cache,
                                        Package,
                                        Refresh,
                                        [Current_output]
                                    )
                            end end
                    )
                end
            )
        end
    ).

-file("src/gleamdoc/index.gleam", 840).
-spec line_at_offset_graphemes(list(binary()), integer(), integer(), integer()) -> integer().
line_at_offset_graphemes(Graphemes, Offset, Current_offset, Line) ->
    case Graphemes of
        [] ->
            Line;

        _ when Current_offset >= Offset ->
            Line;

        [Grapheme | Rest] ->
            line_at_offset_graphemes(
                Rest,
                Offset,
                Current_offset + erlang:byte_size(Grapheme),
                case Grapheme of
                    <<"\n"/utf8>> ->
                        Line + 1;

                    <<"\r\n"/utf8>> ->
                        Line + 1;

                    _ ->
                        Line
                end
            )
    end.

-file("src/gleamdoc/index.gleam", 836).
-spec line_at_offset(binary(), integer()) -> integer().
line_at_offset(Source, Offset) ->
    line_at_offset_graphemes(gleam@string:to_graphemes(Source), Offset, 0, 1).

-file("src/gleamdoc/index.gleam", 862).
-spec relative_path(binary(), binary()) -> binary().
relative_path(Root, Path) ->
    Prefix = <<Root/binary, "/"/utf8>>,
    case gleam_stdlib:string_starts_with(Path, Prefix) of
        true ->
            gleam_stdlib:string_remove_prefix(Path, Prefix);

        false ->
            Path
    end.

-file("src/gleamdoc/index.gleam", 762).
-spec find_variant_token_offset(
    list({glexer@token:token(), glexer:position()}),
    binary(),
    integer(),
    integer(),
    integer(),
    integer()
) -> integer().
find_variant_token_offset(
    Tokens,
    Name,
    Start,
    End,
    Brace_depth,
    Parenthesis_depth
) ->
    case Tokens of
        [] ->
            -1;

        [{_, {position, Offset}} | Rest] when Offset < Start ->
            find_variant_token_offset(
                Rest,
                Name,
                Start,
                End,
                Brace_depth,
                Parenthesis_depth
            );

        [{_, {position, Offset@1}} | _] when Offset@1 > End ->
            -1;

        [{Current, {position, Offset@2}} | Rest@1] ->
            case Current of
                {upper_name, Token_name} when ((Brace_depth =:= 1) andalso (Parenthesis_depth =:= 0)) andalso (Token_name =:= Name) ->
                    Offset@2;

                left_brace ->
                    find_variant_token_offset(
                        Rest@1,
                        Name,
                        Start,
                        End,
                        Brace_depth + 1,
                        Parenthesis_depth
                    );

                right_brace ->
                    find_variant_token_offset(
                        Rest@1,
                        Name,
                        Start,
                        End,
                        Brace_depth - 1,
                        Parenthesis_depth
                    );

                left_paren ->
                    find_variant_token_offset(
                        Rest@1,
                        Name,
                        Start,
                        End,
                        Brace_depth,
                        Parenthesis_depth + 1
                    );

                right_paren ->
                    find_variant_token_offset(
                        Rest@1,
                        Name,
                        Start,
                        End,
                        Brace_depth,
                        Parenthesis_depth - 1
                    );

                _ ->
                    find_variant_token_offset(
                        Rest@1,
                        Name,
                        Start,
                        End,
                        Brace_depth,
                        Parenthesis_depth
                    )
            end
    end.

-file("src/gleamdoc/index.gleam", 754).
-spec variant_exists(list(glance:variant()), binary()) -> boolean().
variant_exists(Variants, Name) ->
    case Variants of
        [] ->
            false;

        [{variant, Variant_name, _, _} | Rest] ->
            (Variant_name =:= Name) orelse variant_exists(Rest, Name)
    end.

-file("src/gleamdoc/index.gleam", 738).
-spec find_custom_type_with_variant(
    list(glance:definition(glance:custom_type())),
    binary()
) -> gleam@option:option(glance:custom_type()).
find_custom_type_with_variant(Definitions, Name) ->
    case Definitions of
        [] ->
            none;

        [{definition, _, Custom_type} | Rest] ->
            {custom_type, _, _, _, _, _, Variants} = Custom_type,
            case variant_exists(Variants, Name) of
                true ->
                    {some, Custom_type};

                false ->
                    find_custom_type_with_variant(Rest, Name)
            end
    end.

-file("src/gleamdoc/index.gleam", 704).
-spec find_variant_offset(binary(), glance:module_(), binary()) -> integer().
find_variant_offset(Source, Module, Name) ->
    {module, _, Custom_types, _, _, _} = Module,
    case find_custom_type_with_variant(Custom_types, Name) of
        none ->
            -1;

        {some, {custom_type, {span, Start, End}, _, _, _, _, _}} ->
            _pipe = Source,
            _pipe@1 = glexer:new(_pipe),
            _pipe@2 = glexer:discard_whitespace(_pipe@1),
            _pipe@3 = glexer:discard_comments(_pipe@2),
            _pipe@4 = glexer:lex(_pipe@3),
            find_variant_token_offset(_pipe@4, Name, Start, End, 0, 0)
    end.

-file("src/gleamdoc/index.gleam", 680).
-spec find_type_alias_offset(
    list(glance:definition(glance:type_alias())),
    binary()
) -> integer().
find_type_alias_offset(Definitions, Name) ->
    case Definitions of
        [] ->
            -1;

        [{definition,
                _,
                {type_alias, {span, Start, _}, Definition_name, _, _, _}} |
            Rest] ->
            case Definition_name =:= Name of
                true ->
                    Start;

                false ->
                    find_type_alias_offset(Rest, Name)
            end
    end.

-file("src/gleamdoc/index.gleam", 722).
-spec find_custom_type(list(glance:definition(glance:custom_type())), binary()) -> gleam@option:option(glance:custom_type()).
find_custom_type(Definitions, Name) ->
    case Definitions of
        [] ->
            none;

        [{definition, _, Custom_type} | Rest] ->
            {custom_type, _, Definition_name, _, _, _, _} = Custom_type,
            case Definition_name =:= Name of
                true ->
                    {some, Custom_type};

                false ->
                    find_custom_type(Rest, Name)
            end
    end.

-file("src/gleamdoc/index.gleam", 672).
-spec find_type_offset(glance:module_(), binary()) -> integer().
find_type_offset(Module, Name) ->
    {module, _, Custom_types, Type_aliases, _, _} = Module,
    case find_custom_type(Custom_types, Name) of
        {some, {custom_type, {span, Start, _}, _, _, _, _, _}} ->
            Start;

        none ->
            find_type_alias_offset(Type_aliases, Name)
    end.

-file("src/gleamdoc/index.gleam", 648).
-spec find_constant_offset(list(glance:definition(glance:constant())), binary()) -> integer().
find_constant_offset(Definitions, Name) ->
    case Definitions of
        [] ->
            -1;

        [{definition, _, {constant, {span, Start, _}, Definition_name, _, _, _}} |
            Rest] ->
            case Definition_name =:= Name of
                true ->
                    Start;

                false ->
                    find_constant_offset(Rest, Name)
            end
    end.

-file("src/gleamdoc/index.gleam", 624).
-spec find_function_offset(
    list(glance:definition(glance:function_())),
    binary()
) -> integer().
find_function_offset(Definitions, Name) ->
    case Definitions of
        [] ->
            -1;

        [{definition,
                _,
                {function, {span, Start, _}, Definition_name, _, _, _, _}} |
            Rest] ->
            case Definition_name =:= Name of
                true ->
                    Start;

                false ->
                    find_function_offset(Rest, Name)
            end
    end.

-file("src/gleamdoc/index.gleam", 608).
-spec declaration_offset(binary(), binary(), binary()) -> integer().
declaration_offset(Source, Kind, Name) ->
    case {Kind, glance:module(Source)} of
        {<<"module"/utf8>>, _} ->
            0;

        {_, {error, _}} ->
            -1;

        {<<"fn"/utf8>>, {ok, {module, _, _, _, _, Functions}}} ->
            find_function_offset(Functions, Name);

        {<<"const"/utf8>>, {ok, {module, _, _, _, Constants, _}}} ->
            find_constant_offset(Constants, Name);

        {<<"type"/utf8>>, {ok, Module}} ->
            find_type_offset(Module, Name);

        {<<"alias"/utf8>>, {ok, {module, _, _, Type_aliases, _, _}}} ->
            find_type_alias_offset(Type_aliases, Name);

        {<<"variant"/utf8>>, {ok, Module@1}} ->
            find_variant_offset(Source, Module@1, Name);

        {_, _} ->
            -1
    end.

-file("src/gleamdoc/index.gleam", 600).
-spec current_project_name(binary()) -> gleam@option:option(binary()).
current_project_name(Root) ->
    Config = filepath:join(Root, <<"gleam.toml"/utf8>>),
    case simplifile:read(Config) of
        {ok, Content} ->
            package_name(Content);

        {error, _} ->
            none
    end.

-file("src/gleamdoc/index.gleam", 570).
-spec source_location_from(binary(), binary(), binary(), binary(), binary()) -> binary().
source_location_from(Root, Package, Module, Kind, Name) ->
    Dependency = begin
        _pipe = join3(Root, <<"build"/utf8>>, <<"packages"/utf8>>),
        filepath:join(_pipe, Package)
    end,
    Source_root = case current_project_name(Root) =:= {some, Package} of
        true ->
            filepath:join(Root, <<"src"/utf8>>);

        false ->
            case simplifile_erl:is_directory(Dependency) of
                {ok, true} ->
                    filepath:join(Dependency, <<"src"/utf8>>);

                _ ->
                    <<""/utf8>>
            end
    end,
    Path = filepath:join(Source_root, <<Module/binary, ".gleam"/utf8>>),
    case simplifile:read(Path) of
        {error, _} ->
            <<""/utf8>>;

        {ok, Source} ->
            case declaration_offset(Source, Kind, Name) of
                -1 ->
                    <<""/utf8>>;

                Offset ->
                    <<<<(relative_path(Root, Path))/binary, ":"/utf8>>/binary,
                        (erlang:integer_to_binary(
                            line_at_offset(Source, Offset)
                        ))/binary>>
            end
    end.

-file("src/gleamdoc/index.gleam", 558).
?DOC(
    " Returns the source path and declaration line for an indexed entry.\n"
    "\n"
    " An empty string is returned when source metadata is unavailable. Source\n"
    " locations are supplementary and do not make an otherwise valid lookup fail.\n"
).
-spec source_location(binary(), binary(), binary(), binary()) -> binary().
source_location(Package, Module, Kind, Name) ->
    case simplifile:current_directory() of
        {error, _} ->
            <<""/utf8>>;

        {ok, Root} ->
            source_location_from(Root, Package, Module, Kind, Name)
    end.