Skip to main content

src/oaisp@internal@merge.erl

-module(oaisp@internal@merge).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaisp/internal/merge.gleam").
-export([to_string/3, unresolved_refs/2, malformed_formats/2, duplicate_routes/1, path_param_mismatches/1]).
-export_type([resolved_component/0, path_param_mismatch/0, oas/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 resolved_component() :: {resolved_component,
        binary(),
        oaisp@internal@package_interface:resolved_type()}.

-type path_param_mismatch() :: {param_without_placeholder,
        binary(),
        binary(),
        binary()} |
    {placeholder_without_param, binary(), binary(), binary()}.

-type oas() :: o_string |
    o_integer |
    o_number |
    o_boolean |
    o_null |
    {o_array, oas()} |
    {o_tuple, list(oas())} |
    {o_map, oas()} |
    {o_object,
        list({binary(), oas()}),
        list(binary()),
        gleam@option:option(binary())} |
    {o_enum, list(binary()), gleam@option:option(binary())} |
    {o_ref, binary()} |
    {o_nullable, oas()} |
    {o_any, gleam@option:option(binary())} |
    {o_string_format, binary()}.

-file("src/oaisp/internal/merge.gleam", 334).
?DOC(false).
-spec ref_key(binary(), binary()) -> binary().
ref_key(Module, Name) ->
    <<<<Module/binary, "#"/utf8>>/binary, Name/binary>>.

-file("src/oaisp/internal/merge.gleam", 649).
?DOC(false).
-spec ref_or_any(binary(), binary(), gleam@dict:dict(binary(), binary())) -> oas().
ref_or_any(Module, Name, Component_refs) ->
    case gleam_stdlib:map_get(Component_refs, ref_key(Module, Name)) of
        {ok, Component_name} ->
            {o_ref, Component_name};

        {error, nil} ->
            {o_any, none}
    end.

-file("src/oaisp/internal/merge.gleam", 602).
?DOC(false).
-spec field_oas(
    oaisp@internal@package_interface:field_type(),
    gleam@dict:dict(binary(), binary())
) -> oas().
field_oas(Field_type, Component_refs) ->
    case Field_type of
        string_type ->
            o_string;

        int_type ->
            o_integer;

        float_type ->
            o_number;

        bool_type ->
            o_boolean;

        nil_type ->
            o_null;

        {list_type, Element} ->
            {o_array, field_oas(Element, Component_refs)};

        {option_type, Inner} ->
            {o_nullable, field_oas(Inner, Component_refs)};

        {dict_type, Value} ->
            {o_map, field_oas(Value, Component_refs)};

        {tuple_type, Elements} ->
            {o_tuple,
                gleam@list:map(
                    Elements,
                    fun(_capture) -> field_oas(_capture, Component_refs) end
                )};

        {formatted_string_type, Format} ->
            {o_string_format, Format};

        timestamp_type ->
            {o_string_format, <<"date-time"/utf8>>};

        {ref_type, Module, Name} ->
            ref_or_any(Module, Name, Component_refs);

        any_type ->
            {o_any, none}
    end.

-file("src/oaisp/internal/merge.gleam", 577).
?DOC(false).
-spec resolved_oas(
    oaisp@internal@package_interface:resolved_type(),
    gleam@dict:dict(binary(), binary())
) -> oas().
resolved_oas(Resolved, Component_refs) ->
    case Resolved of
        {record_type, Fields, Documentation} ->
            Properties = gleam@list:map(
                Fields,
                fun(Field) ->
                    {erlang:element(2, Field),
                        field_oas(erlang:element(3, Field), Component_refs)}
                end
            ),
            Required = gleam@list:filter_map(
                Fields,
                fun(Field@1) -> case erlang:element(3, Field@1) of
                        {option_type, _} ->
                            {error, nil};

                        _ ->
                            {ok, erlang:element(2, Field@1)}
                    end end
            ),
            {o_object, Properties, Required, Documentation};

        {enum_type, Variants, Documentation@1} ->
            {o_enum, Variants, Documentation@1};

        {unmodelled, Documentation@2} ->
            {o_any, Documentation@2}
    end.

-file("src/oaisp/internal/merge.gleam", 701).
?DOC(false).
-spec string_format_schema(binary()) -> gleam@json:json().
string_format_schema(Format) ->
    gleam@json:object(
        [{<<"type"/utf8>>, gleam@json:string(<<"string"/utf8>>)},
            {<<"format"/utf8>>, gleam@json:string(Format)}]
    ).

-file("src/oaisp/internal/merge.gleam", 708).
?DOC(false).
-spec typed(binary()) -> gleam@json:json().
typed(Name) ->
    gleam@json:object([{<<"type"/utf8>>, gleam@json:string(Name)}]).

-file("src/oaisp/internal/merge.gleam", 778).
?DOC(false).
-spec type_array(list(binary())) -> gleam@json:json().
type_array(Types) ->
    gleam@json:object(
        [{<<"type"/utf8>>, gleam@json:array(Types, fun gleam@json:string/1)}]
    ).

-file("src/oaisp/internal/merge.gleam", 782).
?DOC(false).
-spec optional(binary(), gleam@option:option(binary())) -> list({binary(),
    gleam@json:json()}).
optional(Key, Value) ->
    case Value of
        none ->
            [];

        {some, Text} ->
            [{Key, gleam@json:string(Text)}]
    end.

-file("src/oaisp/internal/merge.gleam", 743).
?DOC(false).
-spec enum_to_json(list(binary()), gleam@option:option(binary())) -> gleam@json:json().
enum_to_json(Values, Description) ->
    gleam@json:object(
        lists:append(
            [optional(<<"description"/utf8>>, Description),
                [{<<"type"/utf8>>, gleam@json:string(<<"string"/utf8>>)},
                    {<<"enum"/utf8>>,
                        gleam@json:array(Values, fun gleam@json:string/1)}]]
        )
    ).

-file("src/oaisp/internal/merge.gleam", 712).
?DOC(false).
-spec number_schema() -> gleam@json:json().
number_schema() ->
    gleam@json:object(
        [{<<"type"/utf8>>, gleam@json:string(<<"number"/utf8>>)},
            {<<"format"/utf8>>, gleam@json:string(<<"double"/utf8>>)}]
    ).

-file("src/oaisp/internal/merge.gleam", 755).
?DOC(false).
-spec nullable_to_json(oas()) -> gleam@json:json().
nullable_to_json(Inner) ->
    case Inner of
        o_string ->
            type_array([<<"string"/utf8>>, <<"null"/utf8>>]);

        o_integer ->
            type_array([<<"integer"/utf8>>, <<"null"/utf8>>]);

        o_number ->
            gleam@json:object(
                [{<<"type"/utf8>>,
                        gleam@json:array(
                            [<<"number"/utf8>>, <<"null"/utf8>>],
                            fun gleam@json:string/1
                        )},
                    {<<"format"/utf8>>, gleam@json:string(<<"double"/utf8>>)}]
            );

        o_boolean ->
            type_array([<<"boolean"/utf8>>, <<"null"/utf8>>]);

        o_null ->
            typed(<<"null"/utf8>>);

        {o_string_format, Format} ->
            gleam@json:object(
                [{<<"type"/utf8>>,
                        gleam@json:array(
                            [<<"string"/utf8>>, <<"null"/utf8>>],
                            fun gleam@json:string/1
                        )},
                    {<<"format"/utf8>>, gleam@json:string(Format)}]
            );

        Other ->
            gleam@json:object(
                [{<<"anyOf"/utf8>>,
                        gleam@json:preprocessed_array(
                            [oas_to_json(Other), typed(<<"null"/utf8>>)]
                        )}]
            )
    end.

-file("src/oaisp/internal/merge.gleam", 719).
?DOC(false).
-spec object_to_json(
    list({binary(), oas()}),
    list(binary()),
    gleam@option:option(binary())
) -> gleam@json:json().
object_to_json(Properties, Required, Description) ->
    Required_part = case Required of
        [] ->
            [];

        Names ->
            [{<<"required"/utf8>>,
                    gleam@json:array(Names, fun gleam@json:string/1)}]
    end,
    gleam@json:object(
        lists:append(
            [optional(<<"description"/utf8>>, Description),
                [{<<"type"/utf8>>, gleam@json:string(<<"object"/utf8>>)},
                    {<<"properties"/utf8>>,
                        gleam@json:object(
                            gleam@list:map(
                                Properties,
                                fun(P) ->
                                    {erlang:element(1, P),
                                        oas_to_json(erlang:element(2, P))}
                                end
                            )
                        )}],
                Required_part]
        )
    ).

-file("src/oaisp/internal/merge.gleam", 660).
?DOC(false).
-spec oas_to_json(oas()) -> gleam@json:json().
oas_to_json(Oas) ->
    case Oas of
        o_string ->
            typed(<<"string"/utf8>>);

        o_integer ->
            typed(<<"integer"/utf8>>);

        o_number ->
            number_schema();

        o_boolean ->
            typed(<<"boolean"/utf8>>);

        o_null ->
            typed(<<"null"/utf8>>);

        {o_array, Items} ->
            gleam@json:object(
                [{<<"type"/utf8>>, gleam@json:string(<<"array"/utf8>>)},
                    {<<"items"/utf8>>, oas_to_json(Items)}]
            );

        {o_tuple, Elements} ->
            gleam@json:object(
                [{<<"type"/utf8>>, gleam@json:string(<<"array"/utf8>>)},
                    {<<"prefixItems"/utf8>>,
                        gleam@json:array(Elements, fun oas_to_json/1)}]
            );

        {o_map, Value} ->
            gleam@json:object(
                [{<<"type"/utf8>>, gleam@json:string(<<"object"/utf8>>)},
                    {<<"additionalProperties"/utf8>>, oas_to_json(Value)}]
            );

        {o_object, Properties, Required, Description} ->
            object_to_json(Properties, Required, Description);

        {o_enum, Values, Description@1} ->
            enum_to_json(Values, Description@1);

        {o_ref, Name} ->
            gleam@json:object(
                [{<<"$ref"/utf8>>,
                        gleam@json:string(
                            <<"#/components/schemas/"/utf8, Name/binary>>
                        )}]
            );

        {o_nullable, Inner} ->
            nullable_to_json(Inner);

        {o_any, Description@2} ->
            case Description@2 of
                none ->
                    gleam@json:object([]);

                {some, Text} ->
                    gleam@json:object(
                        [{<<"description"/utf8>>, gleam@json:string(Text)}]
                    )
            end;

        {o_string_format, Format} ->
            string_format_schema(Format)
    end.

-file("src/oaisp/internal/merge.gleam", 338).
?DOC(false).
-spec components_object(
    gleam@dict:dict(binary(), resolved_component()),
    gleam@dict:dict(binary(), binary())
) -> gleam@json:json().
components_object(Components, Component_refs) ->
    _pipe = Components,
    _pipe@1 = maps:to_list(_pipe),
    _pipe@2 = gleam@list:map(
        _pipe@1,
        fun(Entry) ->
            Component_name@1 = case gleam_stdlib:map_get(
                Component_refs,
                erlang:element(1, Entry)
            ) of
                {ok, Component_name} -> Component_name;
                _assert_fail ->
                    erlang:error(#{gleam_error => let_assert,
                                message => <<"component_refs are derived from the same component map"/utf8>>,
                                file => <<?FILEPATH/utf8>>,
                                module => <<"oaisp/internal/merge"/utf8>>,
                                function => <<"components_object"/utf8>>,
                                line => 345,
                                value => _assert_fail,
                                start => 9708,
                                'end' => 9773,
                                pattern_start => 9719,
                                pattern_end => 9737})
            end,
            {Component_name@1,
                oas_to_json(
                    resolved_oas(
                        erlang:element(3, erlang:element(2, Entry)),
                        Component_refs
                    )
                )}
        end
    ),
    _pipe@3 = gleam@list:sort(
        _pipe@2,
        fun(A, B) ->
            gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
        end
    ),
    gleam@json:object(_pipe@3).

-file("src/oaisp/internal/merge.gleam", 640).
?DOC(false).
-spec scalar_oas(oaisp@schema:scalar_kind()) -> oas().
scalar_oas(Kind) ->
    case Kind of
        string_kind ->
            o_string;

        int_kind ->
            o_integer;

        bool_kind ->
            o_boolean;

        float_kind ->
            o_number
    end.

-file("src/oaisp/internal/merge.gleam", 621).
?DOC(false).
-spec schema_oas(oaisp@schema:schema(), gleam@dict:dict(binary(), binary())) -> oas().
schema_oas(Schema, Component_refs) ->
    case Schema of
        {type_ref, Module, Name} ->
            ref_or_any(Module, Name, Component_refs);

        {scalar, Kind, _} ->
            scalar_oas(Kind)
    end.

-file("src/oaisp/internal/merge.gleam", 282).
?DOC(false).
-spec content(oas()) -> gleam@json:json().
content(Schema) ->
    gleam@json:object(
        [{<<"application/json"/utf8>>,
                gleam@json:object([{<<"schema"/utf8>>, oas_to_json(Schema)}])}]
    ).

-file("src/oaisp/internal/merge.gleam", 789).
?DOC(false).
-spec status_reason(integer()) -> binary().
status_reason(Status) ->
    case Status of
        200 ->
            <<"OK"/utf8>>;

        201 ->
            <<"Created"/utf8>>;

        202 ->
            <<"Accepted"/utf8>>;

        204 ->
            <<"No Content"/utf8>>;

        301 ->
            <<"Moved Permanently"/utf8>>;

        400 ->
            <<"Bad Request"/utf8>>;

        401 ->
            <<"Unauthorized"/utf8>>;

        403 ->
            <<"Forbidden"/utf8>>;

        404 ->
            <<"Not Found"/utf8>>;

        409 ->
            <<"Conflict"/utf8>>;

        422 ->
            <<"Unprocessable Entity"/utf8>>;

        429 ->
            <<"Too Many Requests"/utf8>>;

        500 ->
            <<"Internal Server Error"/utf8>>;

        _ ->
            <<"Response"/utf8>>
    end.

-file("src/oaisp/internal/merge.gleam", 262).
?DOC(false).
-spec response_object(
    oaisp@endpoint:response(),
    gleam@dict:dict(binary(), binary())
) -> gleam@json:json().
response_object(Response, Component_refs) ->
    Description = gleam@option:unwrap(
        erlang:element(4, Response),
        status_reason(erlang:element(2, Response))
    ),
    Base = [{<<"description"/utf8>>, gleam@json:string(Description)}],
    case erlang:element(3, Response) of
        none ->
            gleam@json:object(Base);

        {some, Schema} ->
            gleam@json:object(
                lists:append(
                    Base,
                    [{<<"content"/utf8>>,
                            content(schema_oas(Schema, Component_refs))}]
                )
            )
    end.

-file("src/oaisp/internal/merge.gleam", 251).
?DOC(false).
-spec responses_object(
    list(oaisp@endpoint:response()),
    gleam@dict:dict(binary(), binary())
) -> gleam@json:json().
responses_object(Responses, Component_refs) ->
    _pipe = Responses,
    _pipe@1 = gleam@list:map(
        _pipe,
        fun(Response) ->
            {erlang:integer_to_binary(erlang:element(2, Response)),
                response_object(Response, Component_refs)}
        end
    ),
    gleam@json:object(_pipe@1).

-file("src/oaisp/internal/merge.gleam", 244).
?DOC(false).
-spec request_body_object(
    oaisp@schema:schema(),
    gleam@dict:dict(binary(), binary())
) -> gleam@json:json().
request_body_object(Schema, Component_refs) ->
    gleam@json:object(
        [{<<"required"/utf8>>, gleam@json:bool(true)},
            {<<"content"/utf8>>, content(schema_oas(Schema, Component_refs))}]
    ).

-file("src/oaisp/internal/merge.gleam", 235).
?DOC(false).
-spec query_parameter_json(binary(), oas(), boolean()) -> gleam@json:json().
query_parameter_json(Name, Oas, Required) ->
    gleam@json:object(
        [{<<"name"/utf8>>, gleam@json:string(Name)},
            {<<"in"/utf8>>, gleam@json:string(<<"query"/utf8>>)},
            {<<"required"/utf8>>, gleam@json:bool(Required)},
            {<<"schema"/utf8>>, oas_to_json(Oas)}]
    ).

-file("src/oaisp/internal/merge.gleam", 222).
?DOC(false).
-spec query_scalar_oas(oaisp@internal@package_interface:field_type()) -> {ok,
        oas()} |
    {error, nil}.
query_scalar_oas(Field_type) ->
    case Field_type of
        string_type ->
            {ok, o_string};

        int_type ->
            {ok, o_integer};

        float_type ->
            {ok, o_number};

        bool_type ->
            {ok, o_boolean};

        {list_type, Element} ->
            _pipe = query_scalar_oas(Element),
            gleam@result:map(_pipe, fun(Field@0) -> {o_array, Field@0} end);

        {formatted_string_type, Format} ->
            {ok, {o_string_format, Format}};

        timestamp_type ->
            {ok, {o_string_format, <<"date-time"/utf8>>}};

        _ ->
            {error, nil}
    end.

-file("src/oaisp/internal/merge.gleam", 211).
?DOC(false).
-spec reflected_query_param(oaisp@internal@package_interface:field()) -> {ok,
        gleam@json:json()} |
    {error, nil}.
reflected_query_param(Field) ->
    {Base, Required} = case erlang:element(3, Field) of
        {option_type, Inner} ->
            {Inner, false};

        Other ->
            {Other, true}
    end,
    _pipe = query_scalar_oas(Base),
    gleam@result:map(
        _pipe,
        fun(_capture) ->
            query_parameter_json(erlang:element(2, Field), _capture, Required)
        end
    ).

-file("src/oaisp/internal/merge.gleam", 196).
?DOC(false).
-spec reflected_query_params(
    gleam@option:option(oaisp@schema:schema()),
    gleam@package_interface:package()
) -> list(gleam@json:json()).
reflected_query_params(Query_record, Package) ->
    case Query_record of
        {some, {type_ref, Module, Name}} ->
            case oaisp@internal@package_interface:resolve_type(
                Package,
                Module,
                Name
            ) of
                {ok, {record_type, Fields, _}} ->
                    gleam@list:filter_map(Fields, fun reflected_query_param/1);

                _ ->
                    []
            end;

        _ ->
            []
    end.

-file("src/oaisp/internal/merge.gleam", 630).
?DOC(false).
-spec param_schema(oaisp@schema:schema(), gleam@dict:dict(binary(), binary())) -> {oas(),
    gleam@option:option(binary())}.
param_schema(Schema, Component_refs) ->
    case Schema of
        {scalar, Kind, Description} ->
            {scalar_oas(Kind), Description};

        {type_ref, Module, Name} ->
            {ref_or_any(Module, Name, Component_refs), none}
    end.

-file("src/oaisp/internal/merge.gleam", 173).
?DOC(false).
-spec parameter(
    oaisp@endpoint:param(),
    binary(),
    gleam@dict:dict(binary(), binary())
) -> gleam@json:json().
parameter(Param, Location, Component_refs) ->
    {Schema, Description} = param_schema(
        erlang:element(3, Param),
        Component_refs
    ),
    gleam@json:object(
        lists:append(
            [[{<<"name"/utf8>>, gleam@json:string(erlang:element(2, Param))},
                    {<<"in"/utf8>>, gleam@json:string(Location)}],
                optional(<<"description"/utf8>>, Description),
                [{<<"required"/utf8>>,
                        gleam@json:bool(erlang:element(4, Param))},
                    {<<"schema"/utf8>>, oas_to_json(Schema)}]]
        )
    ).

-file("src/oaisp/internal/merge.gleam", 134).
?DOC(false).
-spec operation(
    oaisp@endpoint:endpoint(),
    gleam@dict:dict(binary(), binary()),
    gleam@package_interface:package()
) -> gleam@json:json().
operation(E, Component_refs, Package) ->
    Tags@1 = case oaisp@endpoint:tags(E) of
        [] ->
            [];

        Tags ->
            [{<<"tags"/utf8>>, gleam@json:array(Tags, fun gleam@json:string/1)}]
    end,
    Parameters = lists:append(
        [gleam@list:map(
                oaisp@endpoint:path_params(E),
                fun(_capture) ->
                    parameter(_capture, <<"path"/utf8>>, Component_refs)
                end
            ),
            gleam@list:map(
                oaisp@endpoint:query_params(E),
                fun(_capture@1) ->
                    parameter(_capture@1, <<"query"/utf8>>, Component_refs)
                end
            ),
            reflected_query_params(oaisp@endpoint:query_record(E), Package)]
    ),
    Parameters_part = case Parameters of
        [] ->
            [];

        Ps ->
            [{<<"parameters"/utf8>>, gleam@json:preprocessed_array(Ps)}]
    end,
    Request_body = case oaisp@endpoint:body(E) of
        none ->
            [];

        {some, Schema} ->
            [{<<"requestBody"/utf8>>,
                    request_body_object(Schema, Component_refs)}]
    end,
    gleam@json:object(
        lists:append(
            [Tags@1,
                optional(<<"summary"/utf8>>, oaisp@endpoint:summary(E)),
                optional(<<"description"/utf8>>, oaisp@endpoint:description(E)),
                optional(<<"operationId"/utf8>>, oaisp@endpoint:operation_id(E)),
                Parameters_part,
                Request_body,
                [{<<"responses"/utf8>>,
                        responses_object(
                            oaisp@endpoint:responses(E),
                            Component_refs
                        )}]]
        )
    ).

-file("src/oaisp/internal/merge.gleam", 119).
?DOC(false).
-spec path_item(
    list(oaisp@endpoint:endpoint()),
    gleam@dict:dict(binary(), binary()),
    gleam@package_interface:package()
) -> gleam@json:json().
path_item(Endpoints, Component_refs, Package) ->
    _pipe = Endpoints,
    _pipe@1 = gleam@list:map(
        _pipe,
        fun(E) ->
            {oaisp@endpoint:method_to_string(oaisp@endpoint:method(E)),
                operation(E, Component_refs, Package)}
        end
    ),
    gleam@json:object(_pipe@1).

-file("src/oaisp/internal/merge.gleam", 115).
?DOC(false).
-spec ordered_paths(list(oaisp@endpoint:endpoint())) -> list(binary()).
ordered_paths(Endpoints) ->
    _pipe = Endpoints,
    _pipe@1 = gleam@list:map(_pipe, fun oaisp@endpoint:path/1),
    gleam@list:unique(_pipe@1).

-file("src/oaisp/internal/merge.gleam", 102).
?DOC(false).
-spec paths_object(
    list(oaisp@endpoint:endpoint()),
    gleam@dict:dict(binary(), binary()),
    gleam@package_interface:package()
) -> gleam@json:json().
paths_object(Endpoints, Component_refs, Package) ->
    _pipe = ordered_paths(Endpoints),
    _pipe@1 = gleam@list:map(
        _pipe,
        fun(Path) ->
            For_path = gleam@list:filter(
                Endpoints,
                fun(E) -> oaisp@endpoint:path(E) =:= Path end
            ),
            {Path, path_item(For_path, Component_refs, Package)}
        end
    ),
    gleam@json:object(_pipe@1).

-file("src/oaisp/internal/merge.gleam", 84).
?DOC(false).
-spec server_object(binary()) -> gleam@json:json().
server_object(Url) ->
    gleam@json:object([{<<"url"/utf8>>, gleam@json:string(Url)}]).

-file("src/oaisp/internal/merge.gleam", 88).
?DOC(false).
-spec info_object(oaisp@info:info()) -> gleam@json:json().
info_object(Info) ->
    gleam@json:object(
        lists:append(
            [[{<<"title"/utf8>>, gleam@json:string(erlang:element(2, Info))},
                    {<<"version"/utf8>>,
                        gleam@json:string(erlang:element(3, Info))}],
                optional(<<"description"/utf8>>, erlang:element(4, Info))]
        )
    ).

-file("src/oaisp/internal/merge.gleam", 324).
?DOC(false).
-spec namespaced_component_name(binary()) -> binary().
namespaced_component_name(Key) ->
    case gleam@string:split_once(Key, <<"#"/utf8>>) of
        {ok, {Module, Name}} ->
            Module_name = begin
                _pipe = gleam@string:split(Module, <<"/"/utf8>>),
                gleam@string:join(_pipe, <<"."/utf8>>)
            end,
            <<<<Module_name/binary, "."/utf8>>/binary, Name/binary>>;

        {error, nil} ->
            Key
    end.

-file("src/oaisp/internal/merge.gleam", 312).
?DOC(false).
-spec has_duplicate_type_name(
    binary(),
    gleam@dict:dict(binary(), resolved_component())
) -> boolean().
has_duplicate_type_name(Type_name, Components) ->
    Matches = begin
        _pipe = Components,
        _pipe@1 = maps:to_list(_pipe),
        _pipe@2 = gleam@list:filter(
            _pipe@1,
            fun(Entry) ->
                erlang:element(2, erlang:element(2, Entry)) =:= Type_name
            end
        ),
        erlang:length(_pipe@2)
    end,
    Matches > 1.

-file("src/oaisp/internal/merge.gleam", 301).
?DOC(false).
-spec component_name(
    binary(),
    resolved_component(),
    gleam@dict:dict(binary(), resolved_component())
) -> binary().
component_name(Key, Component, Components) ->
    case has_duplicate_type_name(erlang:element(2, Component), Components) of
        false ->
            erlang:element(2, Component);

        true ->
            namespaced_component_name(Key)
    end.

-file("src/oaisp/internal/merge.gleam", 290).
?DOC(false).
-spec component_index(gleam@dict:dict(binary(), resolved_component())) -> gleam@dict:dict(binary(), binary()).
component_index(Components) ->
    _pipe = Components,
    _pipe@1 = maps:to_list(_pipe),
    _pipe@2 = gleam@list:map(
        _pipe@1,
        fun(Entry) ->
            {erlang:element(1, Entry),
                component_name(
                    erlang:element(1, Entry),
                    erlang:element(2, Entry),
                    Components
                )}
        end
    ),
    maps:from_list(_pipe@2).

-file("src/oaisp/internal/merge.gleam", 421).
?DOC(false).
-spec seed_refs(list(oaisp@endpoint:endpoint())) -> list({binary(), binary()}).
seed_refs(Endpoints) ->
    gleam@list:flat_map(Endpoints, fun oaisp@endpoint:type_refs/1).

-file("src/oaisp/internal/merge.gleam", 408).
?DOC(false).
-spec field_type_refs(oaisp@internal@package_interface:field_type()) -> list({binary(),
    binary()}).
field_type_refs(Field_type) ->
    case Field_type of
        {ref_type, Module, Name} ->
            [{Module, Name}];

        {list_type, Element} ->
            field_type_refs(Element);

        {option_type, Inner} ->
            field_type_refs(Inner);

        {dict_type, Value} ->
            field_type_refs(Value);

        {tuple_type, Elements} ->
            gleam@list:flat_map(Elements, fun field_type_refs/1);

        _ ->
            []
    end.

-file("src/oaisp/internal/merge.gleam", 400).
?DOC(false).
-spec child_refs(oaisp@internal@package_interface:resolved_type()) -> list({binary(),
    binary()}).
child_refs(Resolved) ->
    case Resolved of
        {record_type, Fields, _} ->
            gleam@list:flat_map(
                Fields,
                fun(Field) -> field_type_refs(erlang:element(3, Field)) end
            );

        _ ->
            []
    end.

-file("src/oaisp/internal/merge.gleam", 364).
?DOC(false).
-spec do_closure(
    gleam@package_interface:package(),
    list({binary(), binary()}),
    gleam@set:set(binary()),
    gleam@dict:dict(binary(), resolved_component())
) -> gleam@dict:dict(binary(), resolved_component()).
do_closure(Package, Worklist, Visited, Acc) ->
    case Worklist of
        [] ->
            Acc;

        [{Module, Name} | Rest] ->
            Key = ref_key(Module, Name),
            case gleam@set:contains(Visited, Key) of
                true ->
                    do_closure(Package, Rest, Visited, Acc);

                false ->
                    Visited@1 = gleam@set:insert(Visited, Key),
                    case oaisp@internal@package_interface:resolve_type(
                        Package,
                        Module,
                        Name
                    ) of
                        {error, _} ->
                            do_closure(Package, Rest, Visited@1, Acc);

                        {ok, Resolved} ->
                            do_closure(
                                Package,
                                lists:append(child_refs(Resolved), Rest),
                                Visited@1,
                                gleam@dict:insert(
                                    Acc,
                                    Key,
                                    {resolved_component, Name, Resolved}
                                )
                            )
                    end
            end
    end.

-file("src/oaisp/internal/merge.gleam", 357).
?DOC(false).
-spec resolve_closure(
    gleam@package_interface:package(),
    list({binary(), binary()})
) -> gleam@dict:dict(binary(), resolved_component()).
resolve_closure(Package, Seeds) ->
    do_closure(Package, Seeds, gleam@set:new(), maps:new()).

-file("src/oaisp/internal/merge.gleam", 43).
?DOC(false).
-spec to_openapi(
    list(oaisp@endpoint:endpoint()),
    oaisp@info:info(),
    gleam@package_interface:package()
) -> gleam@json:json().
to_openapi(Endpoints, Info, Package) ->
    Components = resolve_closure(Package, seed_refs(Endpoints)),
    Component_refs = component_index(Components),
    Head = [{<<"openapi"/utf8>>, gleam@json:string(<<"3.1.0"/utf8>>)},
        {<<"info"/utf8>>, info_object(Info)}],
    Servers = case erlang:element(5, Info) of
        [] ->
            [];

        Urls ->
            [{<<"servers"/utf8>>, gleam@json:array(Urls, fun server_object/1)}]
    end,
    Paths = [{<<"paths"/utf8>>,
            paths_object(Endpoints, Component_refs, Package)}],
    Components_part = case gleam@dict:is_empty(Components) of
        true ->
            [];

        false ->
            [{<<"components"/utf8>>,
                    gleam@json:object(
                        [{<<"schemas"/utf8>>,
                                components_object(Components, Component_refs)}]
                    )}]
    end,
    gleam@json:object(lists:append([Head, Servers, Paths, Components_part])).

-file("src/oaisp/internal/merge.gleam", 76).
?DOC(false).
-spec to_string(
    list(oaisp@endpoint:endpoint()),
    oaisp@info:info(),
    gleam@package_interface:package()
) -> binary().
to_string(Endpoints, Info, Package) ->
    gleam@json:to_string(to_openapi(Endpoints, Info, Package)).

-file("src/oaisp/internal/merge.gleam", 478).
?DOC(false).
-spec query_record_refs(list(oaisp@endpoint:endpoint())) -> list({binary(),
    binary()}).
query_record_refs(Endpoints) ->
    _pipe = Endpoints,
    _pipe@1 = gleam@list:map(_pipe, fun oaisp@endpoint:query_record/1),
    _pipe@2 = gleam@option:values(_pipe@1),
    gleam@list:filter_map(_pipe@2, fun oaisp@schema:type_ref_parts/1).

-file("src/oaisp/internal/merge.gleam", 442).
?DOC(false).
-spec refs_to_validate(list(oaisp@endpoint:endpoint())) -> list({binary(),
    binary()}).
refs_to_validate(Endpoints) ->
    lists:append(seed_refs(Endpoints), query_record_refs(Endpoints)).

-file("src/oaisp/internal/merge.gleam", 429).
?DOC(false).
-spec unresolved_refs(
    list(oaisp@endpoint:endpoint()),
    gleam@package_interface:package()
) -> list({binary(), binary()}).
unresolved_refs(Endpoints, Package) ->
    _pipe = refs_to_validate(Endpoints),
    _pipe@1 = gleam@list:unique(_pipe),
    gleam@list:filter(
        _pipe@1,
        fun(Ref) ->
            {Module, Name} = Ref,
            oaisp@internal@package_interface:knows_module(Package, Module)
            andalso gleam@result:is_error(
                oaisp@internal@package_interface:resolve_type(
                    Package,
                    Module,
                    Name
                )
            )
        end
    ).

-file("src/oaisp/internal/merge.gleam", 468).
?DOC(false).
-spec reached_refs(
    list(oaisp@endpoint:endpoint()),
    gleam@package_interface:package()
) -> list({binary(), binary()}).
reached_refs(Endpoints, Package) ->
    _pipe = resolve_closure(Package, seed_refs(Endpoints)),
    _pipe@1 = maps:keys(_pipe),
    _pipe@2 = gleam@list:filter_map(
        _pipe@1,
        fun(Key) -> gleam@string:split_once(Key, <<"#"/utf8>>) end
    ),
    lists:append(_pipe@2, query_record_refs(Endpoints)).

-file("src/oaisp/internal/merge.gleam", 453).
?DOC(false).
-spec malformed_formats(
    list(oaisp@endpoint:endpoint()),
    gleam@package_interface:package()
) -> list({binary(), binary(), binary()}).
malformed_formats(Endpoints, Package) ->
    _pipe = reached_refs(Endpoints, Package),
    _pipe@1 = gleam@list:unique(_pipe),
    gleam@list:flat_map(
        _pipe@1,
        fun(Ref) ->
            {Module, Name} = Ref,
            _pipe@2 = oaisp@internal@package_interface:malformed_format_lines(
                Package,
                Module,
                Name
            ),
            gleam@list:map(_pipe@2, fun(Line) -> {Module, Name, Line} end)
        end
    ).

-file("src/oaisp/internal/merge.gleam", 489).
?DOC(false).
-spec duplicate_routes(list(oaisp@endpoint:endpoint())) -> list({binary(),
    binary()}).
duplicate_routes(Endpoints) ->
    Routes = gleam@list:map(
        Endpoints,
        fun(E) ->
            {oaisp@endpoint:method_to_string(oaisp@endpoint:method(E)),
                oaisp@endpoint:path(E)}
        end
    ),
    _pipe = Routes,
    _pipe@1 = gleam@list:filter(
        _pipe,
        fun(Route) ->
            gleam@list:count(Routes, fun(R) -> R =:= Route end) > 1
        end
    ),
    gleam@list:unique(_pipe@1).

-file("src/oaisp/internal/merge.gleam", 541).
?DOC(false).
-spec path_placeholders(binary()) -> list(binary()).
path_placeholders(Path) ->
    _pipe = Path,
    _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
    _pipe@2 = gleam@list:filter(
        _pipe@1,
        fun(Segment) -> Segment /= <<""/utf8>> end
    ),
    gleam@list:filter_map(
        _pipe@2,
        fun(Segment@1) -> _pipe@3 = oaisp@endpoint:placeholder_name(Segment@1),
            gleam@option:to_result(_pipe@3, nil) end
    ).

-file("src/oaisp/internal/merge.gleam", 520).
?DOC(false).
-spec endpoint_path_param_mismatches(oaisp@endpoint:endpoint()) -> list(path_param_mismatch()).
endpoint_path_param_mismatches(E) ->
    Method = oaisp@endpoint:method_to_string(oaisp@endpoint:method(E)),
    Path = oaisp@endpoint:path(E),
    Placeholders = path_placeholders(Path),
    Declared = gleam@list:map(
        oaisp@endpoint:path_params(E),
        fun(Param) -> erlang:element(2, Param) end
    ),
    Params_without_placeholder = begin
        _pipe = Declared,
        _pipe@1 = gleam@list:filter(
            _pipe,
            fun(Name) -> not gleam@list:contains(Placeholders, Name) end
        ),
        gleam@list:map(
            _pipe@1,
            fun(Name@1) -> {param_without_placeholder, Method, Path, Name@1} end
        )
    end,
    Placeholders_without_param = begin
        _pipe@2 = Placeholders,
        _pipe@3 = gleam@list:filter(
            _pipe@2,
            fun(Name@2) -> not gleam@list:contains(Declared, Name@2) end
        ),
        gleam@list:map(
            _pipe@3,
            fun(Name@3) -> {placeholder_without_param, Method, Path, Name@3} end
        )
    end,
    lists:append(Params_without_placeholder, Placeholders_without_param).

-file("src/oaisp/internal/merge.gleam", 514).
?DOC(false).
-spec path_param_mismatches(list(oaisp@endpoint:endpoint())) -> list(path_param_mismatch()).
path_param_mismatches(Endpoints) ->
    gleam@list:flat_map(Endpoints, fun endpoint_path_param_mismatches/1).