Packages

BEAM HTTP adapter for oaspec generated clients (gleam_httpc-backed).

Current section

Files

Jump to
oaspec_httpc src oaspec@httpc.erl
Raw

src/oaspec@httpc.erl

-module(oaspec@httpc).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/oaspec/httpc.gleam").
-export([config/0, with_timeout/2, build/1, send/1]).
-export_type([config/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(
" BEAM HTTP adapter for oaspec generated clients.\n"
"\n"
" `oaspec/httpc` bridges the pure `oaspec/transport.Send` contract\n"
" to `gleam_httpc`. The simplest usage is the bare `send` function:\n"
"\n"
" ```gleam\n"
" import oaspec/httpc\n"
" import api/client\n"
"\n"
" let result = client.list_pets(httpc.send, ...)\n"
" ```\n"
"\n"
" For per-request configuration (timeouts, etc.) use the builder:\n"
"\n"
" ```gleam\n"
" let send =\n"
" httpc.config()\n"
" |> httpc.with_timeout(5_000)\n"
" |> httpc.build\n"
" ```\n"
).
-opaque config() :: {config, gleam@option:option(integer())}.
-file("src/oaspec/httpc.gleam", 40).
-spec config() -> config().
config() ->
{config, none}.
-file("src/oaspec/httpc.gleam", 44).
-spec with_timeout(config(), integer()) -> config().
with_timeout(Cfg, Timeout_ms) ->
_ = Cfg,
{config, {some, Timeout_ms}}.
-file("src/oaspec/httpc.gleam", 110).
-spec encode_query(list({binary(), binary()})) -> binary().
encode_query(Query) ->
case Query of
[] ->
<<""/utf8>>;
_ ->
Parts = gleam@list:map(
Query,
fun(Kv) ->
{K, V} = Kv,
<<<<(gleam_stdlib:percent_encode(K))/binary, "="/utf8>>/binary,
(gleam_stdlib:percent_encode(V))/binary>>
end
),
<<"?"/utf8, (gleam@string:join(Parts, <<"&"/utf8>>))/binary>>
end.
-file("src/oaspec/httpc.gleam", 102).
-spec build_url(oaspec@transport:request()) -> binary().
build_url(Req) ->
Base = case erlang:element(3, Req) of
{some, B} ->
B;
none ->
<<""/utf8>>
end,
<<<<Base/binary, (erlang:element(4, Req))/binary>>/binary,
(encode_query(erlang:element(5, Req)))/binary>>.
-file("src/oaspec/httpc.gleam", 124).
-spec convert_method(oaspec@transport:method()) -> gleam@http:method().
convert_method(Method) ->
case Method of
get ->
get;
post ->
post;
put ->
put;
delete ->
delete;
patch ->
patch;
head ->
head;
options ->
options;
trace ->
trace;
connect ->
connect;
{other, S} ->
{other, S}
end.
-file("src/oaspec/httpc.gleam", 80).
-spec build_http_request(oaspec@transport:request()) -> {ok,
gleam@http@request:request(bitstring())} |
{error, oaspec@transport:transport_error()}.
build_http_request(Req) ->
Url = build_url(Req),
gleam@result:'try'(
begin
_pipe = gleam@http@request:to(Url),
gleam@result:map_error(_pipe, fun(_) -> {invalid_base_url, Url} end)
end,
fun(Parsed) ->
Parsed@1 = gleam@http@request:set_method(
Parsed,
convert_method(erlang:element(2, Req))
),
Parsed@2 = gleam@list:fold(
erlang:element(6, Req),
Parsed@1,
fun(R, H) ->
{Name, Value} = H,
gleam@http@request:set_header(R, Name, Value)
end
),
Body = case erlang:element(7, Req) of
empty_body ->
<<>>;
{text_body, Text} ->
gleam_stdlib:identity(Text);
{bytes_body, Bits} ->
Bits
end,
{ok, gleam@http@request:set_body(Parsed@2, Body)}
end
).
-file("src/oaspec/httpc.gleam", 159).
-spec is_text_content_type(binary()) -> boolean().
is_text_content_type(Ct) ->
Lowered = string:lowercase(Ct),
((gleam_stdlib:contains_string(Lowered, <<"json"/utf8>>) orelse gleam_stdlib:contains_string(
Lowered,
<<"xml"/utf8>>
))
orelse gleam_stdlib:string_starts_with(Lowered, <<"text/"/utf8>>))
orelse gleam_stdlib:contains_string(
Lowered,
<<"x-www-form-urlencoded"/utf8>>
).
-file("src/oaspec/httpc.gleam", 139).
-spec convert_response(gleam@http@response:response(bitstring())) -> oaspec@transport:response().
convert_response(Resp) ->
Is_text = case gleam@list:key_find(
erlang:element(3, Resp),
<<"content-type"/utf8>>
) of
{ok, Ct} ->
is_text_content_type(Ct);
{error, _} ->
true
end,
Body = case Is_text of
true ->
case gleam@bit_array:to_string(erlang:element(4, Resp)) of
{ok, Text} ->
{text_body, Text};
{error, _} ->
{bytes_body, erlang:element(4, Resp)}
end;
false ->
{bytes_body, erlang:element(4, Resp)}
end,
{response, erlang:element(2, Resp), erlang:element(3, Resp), Body}.
-file("src/oaspec/httpc.gleam", 68).
-spec do_send(oaspec@transport:request(), config()) -> {ok,
oaspec@transport:response()} |
{error, oaspec@transport:transport_error()}.
do_send(Req, _) ->
gleam@result:'try'(
build_http_request(Req),
fun(Http_req) -> case gleam@httpc:send_bits(Http_req) of
{ok, Resp} ->
{ok, convert_response(Resp)};
{error, _} ->
{error,
{connection_failed, <<"gleam_httpc send failed"/utf8>>}}
end end
).
-file("src/oaspec/httpc.gleam", 52).
?DOC(
" Materialise a `Send` value from a `Config`. Use this when you've\n"
" configured timeouts or other options. For the simplest case, prefer\n"
" the bare `send` function, which has the same signature as `Send`.\n"
).
-spec build(config()) -> fun((oaspec@transport:request()) -> {ok,
oaspec@transport:response()} |
{error, oaspec@transport:transport_error()}).
build(Cfg) ->
fun(Req) -> do_send(Req, Cfg) end.
-file("src/oaspec/httpc.gleam", 58).
?DOC(
" Convenience: equivalent to `config() |> build`. Provides the most\n"
" common no-config path as a single function reference.\n"
).
-spec send(oaspec@transport:request()) -> {ok, oaspec@transport:response()} |
{error, oaspec@transport:transport_error()}.
send(Req) ->
do_send(Req, config()).