Skip to main content

src/oaisp@endpoint.erl

-module(oaisp@endpoint).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaisp/endpoint.gleam").
-export([get/1, post/1, put/1, patch/1, delete/1, with_body/2, with_response/3, with_empty_response/3, with_path_param/3, with_query_param/4, with_query_record/2, with_summary/2, with_description/2, with_tag/2, with_operation_id/2, method/1, path/1, summary/1, description/1, operation_id/1, tags/1, path_params/1, query_params/1, query_record/1, body/1, responses/1, method_to_string/1, type_refs/1, placeholder_name/1, to_json/1, decoder/0]).
-export_type([method/0, param/0, response/0, endpoint/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(
    " Endpoint declarations: typed values capturing an operation's method, path,\n"
    " parameters, request body, and responses.\n"
    "\n"
    " Each declaration fuses a route with the codecs its handler uses, so the\n"
    " emitted document and the runtime contract are read from one place. An\n"
    " `Endpoint` is built with a method constructor ([`get`](#get),\n"
    " [`post`](#post), …) and refined with the `with_*` combinators; it is opaque\n"
    " so it can never exist without a method and a path.\n"
).

-type method() :: get | post | put | patch | delete.

-type param() :: {param, binary(), oaisp@schema:schema(), boolean()}.

-type response() :: {response,
        integer(),
        gleam@option:option(oaisp@schema:schema()),
        gleam@option:option(binary())}.

-opaque endpoint() :: {endpoint,
        method(),
        binary(),
        gleam@option:option(binary()),
        gleam@option:option(binary()),
        gleam@option:option(binary()),
        list(binary()),
        list(param()),
        list(param()),
        gleam@option:option(oaisp@schema:schema()),
        gleam@option:option(oaisp@schema:schema()),
        list(response())}.

-file("src/oaisp/endpoint.gleam", 58).
-spec new(method(), binary()) -> endpoint().
new(Method, Path) ->
    {endpoint, Method, Path, none, none, none, [], [], [], none, none, []}.

-file("src/oaisp/endpoint.gleam", 75).
?DOC(" A `GET` endpoint at `path`.\n").
-spec get(binary()) -> endpoint().
get(Path) ->
    new(get, Path).

-file("src/oaisp/endpoint.gleam", 80).
?DOC(" A `POST` endpoint at `path`.\n").
-spec post(binary()) -> endpoint().
post(Path) ->
    new(post, Path).

-file("src/oaisp/endpoint.gleam", 85).
?DOC(" A `PUT` endpoint at `path`.\n").
-spec put(binary()) -> endpoint().
put(Path) ->
    new(put, Path).

-file("src/oaisp/endpoint.gleam", 90).
?DOC(" A `PATCH` endpoint at `path`.\n").
-spec patch(binary()) -> endpoint().
patch(Path) ->
    new(patch, Path).

-file("src/oaisp/endpoint.gleam", 95).
?DOC(" A `DELETE` endpoint at `path`.\n").
-spec delete(binary()) -> endpoint().
delete(Path) ->
    new(delete, Path).

-file("src/oaisp/endpoint.gleam", 100).
?DOC(" Attach a request-body schema, taken from `codec`.\n").
-spec with_body(endpoint(), oaisp@schema:schema()) -> endpoint().
with_body(Endpoint, Schema) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        {some, Schema},
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 105).
?DOC(" Document a response with a body schema for `status`.\n").
-spec with_response(endpoint(), integer(), oaisp@schema:schema()) -> endpoint().
with_response(Endpoint, Status, Schema) ->
    Response = {response, Status, {some, Schema}, none},
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        lists:append(erlang:element(12, Endpoint), [Response])}.

-file("src/oaisp/endpoint.gleam", 115).
?DOC(" Document an empty (bodyless) response for `status`, with a description.\n").
-spec with_empty_response(endpoint(), integer(), binary()) -> endpoint().
with_empty_response(Endpoint, Status, Description) ->
    Response = {response, Status, none, {some, Description}},
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        lists:append(erlang:element(12, Endpoint), [Response])}.

-file("src/oaisp/endpoint.gleam", 125).
?DOC(" Document a path parameter. Path parameters are always required.\n").
-spec with_path_param(endpoint(), binary(), oaisp@schema:schema()) -> endpoint().
with_path_param(Endpoint, Name, Schema) ->
    Param = {param, Name, Schema, true},
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        lists:append(erlang:element(8, Endpoint), [Param]),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 135).
?DOC(" Document a query parameter, marking whether it is required.\n").
-spec with_query_param(endpoint(), binary(), oaisp@schema:schema(), boolean()) -> endpoint().
with_query_param(Endpoint, Name, Schema, Required) ->
    Param = {param, Name, Schema, Required},
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        lists:append(erlang:element(9, Endpoint), [Param]),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 153).
?DOC(
    " Document query parameters by reference to a record type: each of the\n"
    " record's scalar fields becomes a query parameter (an `Option` field is\n"
    " optional, the rest required). The type is resolved at merge time from the\n"
    " package interface; fields oaisp can't express as a scalar query parameter\n"
    " are soundly omitted. Mirrors F#'s `addQueryParameters<'T>`.\n"
).
-spec with_query_record(endpoint(), oaisp@schema:schema()) -> endpoint().
with_query_record(Endpoint, Schema) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        {some, Schema},
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 158).
?DOC(" Set the operation summary (a short one-line label).\n").
-spec with_summary(endpoint(), binary()) -> endpoint().
with_summary(Endpoint, Summary) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        {some, Summary},
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 163).
?DOC(" Set the operation description (longer prose).\n").
-spec with_description(endpoint(), binary()) -> endpoint().
with_description(Endpoint, Description) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        {some, Description},
        erlang:element(6, Endpoint),
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 168).
?DOC(" Add a tag, used to group operations in the document.\n").
-spec with_tag(endpoint(), binary()) -> endpoint().
with_tag(Endpoint, Tag) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        erlang:element(6, Endpoint),
        lists:append(erlang:element(7, Endpoint), [Tag]),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 173).
?DOC(" Set the `operationId`, a unique identifier for the operation.\n").
-spec with_operation_id(endpoint(), binary()) -> endpoint().
with_operation_id(Endpoint, Id) ->
    {endpoint,
        erlang:element(2, Endpoint),
        erlang:element(3, Endpoint),
        erlang:element(4, Endpoint),
        erlang:element(5, Endpoint),
        {some, Id},
        erlang:element(7, Endpoint),
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint),
        erlang:element(10, Endpoint),
        erlang:element(11, Endpoint),
        erlang:element(12, Endpoint)}.

-file("src/oaisp/endpoint.gleam", 178).
?DOC(" The endpoint's HTTP method.\n").
-spec method(endpoint()) -> method().
method(Endpoint) ->
    erlang:element(2, Endpoint).

-file("src/oaisp/endpoint.gleam", 183).
?DOC(" The endpoint's path, including any `{param}` placeholders.\n").
-spec path(endpoint()) -> binary().
path(Endpoint) ->
    erlang:element(3, Endpoint).

-file("src/oaisp/endpoint.gleam", 188).
?DOC(" The endpoint's summary, if set.\n").
-spec summary(endpoint()) -> gleam@option:option(binary()).
summary(Endpoint) ->
    erlang:element(4, Endpoint).

-file("src/oaisp/endpoint.gleam", 193).
?DOC(" The endpoint's description, if set.\n").
-spec description(endpoint()) -> gleam@option:option(binary()).
description(Endpoint) ->
    erlang:element(5, Endpoint).

-file("src/oaisp/endpoint.gleam", 198).
?DOC(" The endpoint's `operationId`, if set.\n").
-spec operation_id(endpoint()) -> gleam@option:option(binary()).
operation_id(Endpoint) ->
    erlang:element(6, Endpoint).

-file("src/oaisp/endpoint.gleam", 203).
?DOC(" The endpoint's tags, in declaration order.\n").
-spec tags(endpoint()) -> list(binary()).
tags(Endpoint) ->
    erlang:element(7, Endpoint).

-file("src/oaisp/endpoint.gleam", 208).
?DOC(" The endpoint's path parameters, in declaration order.\n").
-spec path_params(endpoint()) -> list(param()).
path_params(Endpoint) ->
    erlang:element(8, Endpoint).

-file("src/oaisp/endpoint.gleam", 213).
?DOC(" The endpoint's query parameters, in declaration order.\n").
-spec query_params(endpoint()) -> list(param()).
query_params(Endpoint) ->
    erlang:element(9, Endpoint).

-file("src/oaisp/endpoint.gleam", 219).
?DOC(
    " The record type whose scalar fields are reflected into query parameters, if\n"
    " one was set with [`with_query_record`](#with_query_record).\n"
).
-spec query_record(endpoint()) -> gleam@option:option(oaisp@schema:schema()).
query_record(Endpoint) ->
    erlang:element(10, Endpoint).

-file("src/oaisp/endpoint.gleam", 224).
?DOC(" The endpoint's request-body schema, if any.\n").
-spec body(endpoint()) -> gleam@option:option(oaisp@schema:schema()).
body(Endpoint) ->
    erlang:element(11, Endpoint).

-file("src/oaisp/endpoint.gleam", 229).
?DOC(" The endpoint's documented responses, in declaration order.\n").
-spec responses(endpoint()) -> list(response()).
responses(Endpoint) ->
    erlang:element(12, Endpoint).

-file("src/oaisp/endpoint.gleam", 234).
?DOC(" The string used for `method` in an OpenAPI path item (lower-cased).\n").
-spec method_to_string(method()) -> binary().
method_to_string(Method) ->
    case Method of
        get ->
            <<"get"/utf8>>;

        post ->
            <<"post"/utf8>>;

        put ->
            <<"put"/utf8>>;

        patch ->
            <<"patch"/utf8>>;

        delete ->
            <<"delete"/utf8>>
    end.

-file("src/oaisp/endpoint.gleam", 249).
?DOC(false).
-spec type_refs(endpoint()) -> list({binary(), binary()}).
type_refs(Endpoint) ->
    Bodies = [erlang:element(11, Endpoint) |
        gleam@list:map(
            erlang:element(12, Endpoint),
            fun(R) -> erlang:element(3, R) end
        )],
    Params = lists:append(
        erlang:element(8, Endpoint),
        erlang:element(9, Endpoint)
    ),
    _pipe = lists:append(
        gleam@option:values(Bodies),
        gleam@list:map(Params, fun(P) -> erlang:element(3, P) end)
    ),
    _pipe@1 = gleam@list:filter_map(_pipe, fun oaisp@schema:type_ref_parts/1),
    gleam@list:unique(_pipe@1).

-file("src/oaisp/endpoint.gleam", 261).
?DOC(false).
-spec placeholder_name(binary()) -> gleam@option:option(binary()).
placeholder_name(Segment) ->
    case gleam_stdlib:string_starts_with(Segment, <<"{"/utf8>>) andalso gleam_stdlib:string_ends_with(
        Segment,
        <<"}"/utf8>>
    ) of
        true ->
            {some,
                begin
                    _pipe = Segment,
                    _pipe@1 = gleam@string:drop_start(_pipe, 1),
                    gleam@string:drop_end(_pipe@1, 1)
                end};

        false ->
            none
    end.

-file("src/oaisp/endpoint.gleam", 270).
-spec method_from_string(binary()) -> {ok, method()} | {error, nil}.
method_from_string(Name) ->
    case Name of
        <<"get"/utf8>> ->
            {ok, get};

        <<"post"/utf8>> ->
            {ok, post};

        <<"put"/utf8>> ->
            {ok, put};

        <<"patch"/utf8>> ->
            {ok, patch};

        <<"delete"/utf8>> ->
            {ok, delete};

        _ ->
            {error, nil}
    end.

-file("src/oaisp/endpoint.gleam", 281).
-spec method_decoder() -> gleam@dynamic@decode:decoder(method()).
method_decoder() ->
    gleam@dynamic@decode:then(
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Raw) -> case method_from_string(Raw) of
                {ok, Method} ->
                    gleam@dynamic@decode:success(Method);

                {error, nil} ->
                    gleam@dynamic@decode:failure(get, <<"Method"/utf8>>)
            end end
    ).

-file("src/oaisp/endpoint.gleam", 289).
-spec param_to_json(param()) -> gleam@json:json().
param_to_json(Param) ->
    gleam@json:object(
        [{<<"name"/utf8>>, gleam@json:string(erlang:element(2, Param))},
            {<<"required"/utf8>>, gleam@json:bool(erlang:element(4, Param))},
            {<<"schema"/utf8>>,
                oaisp@schema:schema_to_json(erlang:element(3, Param))}]
    ).

-file("src/oaisp/endpoint.gleam", 297).
-spec param_decoder() -> gleam@dynamic@decode:decoder(param()).
param_decoder() ->
    gleam@dynamic@decode:field(
        <<"name"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Name) ->
            gleam@dynamic@decode:field(
                <<"required"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_bool/1},
                fun(Required) ->
                    gleam@dynamic@decode:field(
                        <<"schema"/utf8>>,
                        oaisp@schema:schema_decoder(),
                        fun(Param_schema) ->
                            gleam@dynamic@decode:success(
                                {param, Name, Param_schema, Required}
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/oaisp/endpoint.gleam", 304).
-spec response_to_json(response()) -> gleam@json:json().
response_to_json(Response) ->
    gleam@json:object(
        [{<<"status"/utf8>>, gleam@json:int(erlang:element(2, Response))},
            {<<"body"/utf8>>,
                gleam@json:nullable(
                    erlang:element(3, Response),
                    fun oaisp@schema:schema_to_json/1
                )},
            {<<"description"/utf8>>,
                gleam@json:nullable(
                    erlang:element(4, Response),
                    fun gleam@json:string/1
                )}]
    ).

-file("src/oaisp/endpoint.gleam", 312).
-spec response_decoder() -> gleam@dynamic@decode:decoder(response()).
response_decoder() ->
    gleam@dynamic@decode:field(
        <<"status"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_int/1},
        fun(Status) ->
            gleam@dynamic@decode:field(
                <<"body"/utf8>>,
                gleam@dynamic@decode:optional(oaisp@schema:schema_decoder()),
                fun(Body) ->
                    gleam@dynamic@decode:field(
                        <<"description"/utf8>>,
                        gleam@dynamic@decode:optional(
                            {decoder, fun gleam@dynamic@decode:decode_string/1}
                        ),
                        fun(Description) ->
                            gleam@dynamic@decode:success(
                                {response, Status, Body, Description}
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/oaisp/endpoint.gleam", 321).
?DOC(false).
-spec to_json(endpoint()) -> gleam@json:json().
to_json(Endpoint) ->
    gleam@json:object(
        [{<<"method"/utf8>>,
                gleam@json:string(method_to_string(erlang:element(2, Endpoint)))},
            {<<"path"/utf8>>, gleam@json:string(erlang:element(3, Endpoint))},
            {<<"summary"/utf8>>,
                gleam@json:nullable(
                    erlang:element(4, Endpoint),
                    fun gleam@json:string/1
                )},
            {<<"description"/utf8>>,
                gleam@json:nullable(
                    erlang:element(5, Endpoint),
                    fun gleam@json:string/1
                )},
            {<<"operation_id"/utf8>>,
                gleam@json:nullable(
                    erlang:element(6, Endpoint),
                    fun gleam@json:string/1
                )},
            {<<"tags"/utf8>>,
                gleam@json:array(
                    erlang:element(7, Endpoint),
                    fun gleam@json:string/1
                )},
            {<<"path_params"/utf8>>,
                gleam@json:array(
                    erlang:element(8, Endpoint),
                    fun param_to_json/1
                )},
            {<<"query_params"/utf8>>,
                gleam@json:array(
                    erlang:element(9, Endpoint),
                    fun param_to_json/1
                )},
            {<<"query_record"/utf8>>,
                gleam@json:nullable(
                    erlang:element(10, Endpoint),
                    fun oaisp@schema:schema_to_json/1
                )},
            {<<"body"/utf8>>,
                gleam@json:nullable(
                    erlang:element(11, Endpoint),
                    fun oaisp@schema:schema_to_json/1
                )},
            {<<"responses"/utf8>>,
                gleam@json:array(
                    erlang:element(12, Endpoint),
                    fun response_to_json/1
                )}]
    ).

-file("src/oaisp/endpoint.gleam", 340).
?DOC(false).
-spec decoder() -> gleam@dynamic@decode:decoder(endpoint()).
decoder() ->
    gleam@dynamic@decode:field(
        <<"method"/utf8>>,
        method_decoder(),
        fun(Method) ->
            gleam@dynamic@decode:field(
                <<"path"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_string/1},
                fun(Path) ->
                    gleam@dynamic@decode:field(
                        <<"summary"/utf8>>,
                        gleam@dynamic@decode:optional(
                            {decoder, fun gleam@dynamic@decode:decode_string/1}
                        ),
                        fun(Summary) ->
                            gleam@dynamic@decode:field(
                                <<"description"/utf8>>,
                                gleam@dynamic@decode:optional(
                                    {decoder,
                                        fun gleam@dynamic@decode:decode_string/1}
                                ),
                                fun(Description) ->
                                    gleam@dynamic@decode:field(
                                        <<"operation_id"/utf8>>,
                                        gleam@dynamic@decode:optional(
                                            {decoder,
                                                fun gleam@dynamic@decode:decode_string/1}
                                        ),
                                        fun(Operation_id) ->
                                            gleam@dynamic@decode:field(
                                                <<"tags"/utf8>>,
                                                gleam@dynamic@decode:list(
                                                    {decoder,
                                                        fun gleam@dynamic@decode:decode_string/1}
                                                ),
                                                fun(Tags) ->
                                                    gleam@dynamic@decode:field(
                                                        <<"path_params"/utf8>>,
                                                        gleam@dynamic@decode:list(
                                                            param_decoder()
                                                        ),
                                                        fun(Path_params) ->
                                                            gleam@dynamic@decode:field(
                                                                <<"query_params"/utf8>>,
                                                                gleam@dynamic@decode:list(
                                                                    param_decoder(
                                                                        
                                                                    )
                                                                ),
                                                                fun(
                                                                    Query_params
                                                                ) ->
                                                                    gleam@dynamic@decode:field(
                                                                        <<"query_record"/utf8>>,
                                                                        gleam@dynamic@decode:optional(
                                                                            oaisp@schema:schema_decoder(
                                                                                
                                                                            )
                                                                        ),
                                                                        fun(
                                                                            Query_record
                                                                        ) ->
                                                                            gleam@dynamic@decode:field(
                                                                                <<"body"/utf8>>,
                                                                                gleam@dynamic@decode:optional(
                                                                                    oaisp@schema:schema_decoder(
                                                                                        
                                                                                    )
                                                                                ),
                                                                                fun(
                                                                                    Body
                                                                                ) ->
                                                                                    gleam@dynamic@decode:field(
                                                                                        <<"responses"/utf8>>,
                                                                                        gleam@dynamic@decode:list(
                                                                                            response_decoder(
                                                                                                
                                                                                            )
                                                                                        ),
                                                                                        fun(
                                                                                            Responses
                                                                                        ) ->
                                                                                            gleam@dynamic@decode:success(
                                                                                                {endpoint,
                                                                                                    Method,
                                                                                                    Path,
                                                                                                    Summary,
                                                                                                    Description,
                                                                                                    Operation_id,
                                                                                                    Tags,
                                                                                                    Path_params,
                                                                                                    Query_params,
                                                                                                    Query_record,
                                                                                                    Body,
                                                                                                    Responses}
                                                                                            )
                                                                                        end
                                                                                    )
                                                                                end
                                                                            )
                                                                        end
                                                                    )
                                                                end
                                                            )
                                                        end
                                                    )
                                                end
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).