Current section
Files
Jump to
Current section
Files
src/pig@ai@http.erl
-module(pig@ai@http).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pig/ai/http.gleam").
-export([build_request/3, map_response/1, map_http_error/1, post_with_timeout/4, post/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/pig/ai/http.gleam", 89).
-spec set_headers(
gleam@http@request:request(binary()),
list({binary(), binary()})
) -> gleam@http@request:request(binary()).
set_headers(Req, Headers) ->
case Headers of
[] ->
Req;
[{Key, Value} | Rest] ->
_pipe = Req,
_pipe@1 = gleam@http@request:set_header(_pipe, Key, Value),
set_headers(_pipe@1, Rest)
end.
-file("src/pig/ai/http.gleam", 12).
?DOC(
" Build a POST request from URL, headers, and body.\n"
" Pure — no network IO. Returns Error if URL is malformed.\n"
).
-spec build_request(binary(), list({binary(), binary()}), binary()) -> {ok,
gleam@http@request:request(binary())} |
{error, pig@ai@error:ai_error()}.
build_request(Url, Headers, Body) ->
case gleam@http@request:to(Url) of
{ok, Req} ->
{ok,
begin
_pipe = Req,
_pipe@1 = gleam@http@request:set_method(_pipe, post),
_pipe@2 = set_headers(_pipe@1, Headers),
gleam@http@request:set_body(_pipe@2, Body)
end};
{error, _} ->
{error, {api_error, <<"Invalid URL: "/utf8, Url/binary>>}}
end.
-file("src/pig/ai/http.gleam", 32).
?DOC(
" Map an HTTP response to a Result(String, AiError).\n"
" 2xx -> Ok(body), 429 -> RateLimited, other -> ApiError.\n"
" Pure — no network IO.\n"
).
-spec map_response(gleam@http@response:response(binary())) -> {ok, binary()} |
{error, pig@ai@error:ai_error()}.
map_response(Resp) ->
case erlang:element(2, Resp) of
S when (S >= 200) andalso (S < 300) ->
{ok, erlang:element(4, Resp)};
429 ->
{error, rate_limited};
Status ->
{error,
{api_error,
<<<<<<"HTTP "/utf8,
(erlang:integer_to_binary(Status))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(4, Resp))/binary>>}}
end.
-file("src/pig/ai/http.gleam", 113).
-spec format_socket_error(gleam@httpc:connect_error()) -> binary().
format_socket_error(Err) ->
case Err of
{posix, Code} ->
Code;
{tls_alert, Code@1, Detail} ->
<<<<Code@1/binary, ": "/utf8>>/binary, Detail/binary>>
end.
-file("src/pig/ai/http.gleam", 102).
-spec format_connect_error(gleam@httpc:http_error()) -> binary().
format_connect_error(Err) ->
case Err of
{failed_to_connect, Ip4, Ip6} ->
<<<<<<"ipv4="/utf8, (format_socket_error(Ip4))/binary>>/binary,
" ipv6="/utf8>>/binary,
(format_socket_error(Ip6))/binary>>;
_ ->
<<"unknown connection error"/utf8>>
end.
-file("src/pig/ai/http.gleam", 45).
?DOC(
" Map a gleam_httpc transport error to AiError.\n"
" Pure — no network IO.\n"
).
-spec map_http_error(gleam@httpc:http_error()) -> pig@ai@error:ai_error().
map_http_error(Err) ->
case Err of
response_timeout ->
timeout;
invalid_utf8_response ->
{invalid_response, <<"Response body was not valid UTF-8"/utf8>>};
{failed_to_connect, _, _} ->
{api_error,
<<"Failed to connect: "/utf8,
(format_connect_error(Err))/binary>>}
end.
-file("src/pig/ai/http.gleam", 70).
?DOC(
" Send a POST request with an explicit timeout in milliseconds.\n"
" Returns response body or AiError.\n"
).
-spec post_with_timeout(
binary(),
list({binary(), binary()}),
binary(),
integer()
) -> {ok, binary()} | {error, pig@ai@error:ai_error()}.
post_with_timeout(Url, Headers, Body, Timeout_ms) ->
logging:log(debug, <<"POST "/utf8, Url/binary>>),
gleam@result:'try'(
build_request(Url, Headers, Body),
fun(Req) ->
Config = begin
_pipe = gleam@httpc:configure(),
gleam@httpc:timeout(_pipe, Timeout_ms)
end,
gleam@result:'try'(
begin
_pipe@1 = gleam@httpc:dispatch(Config, Req),
gleam@result:map_error(_pipe@1, fun map_http_error/1)
end,
fun(Resp) ->
logging:log(
debug,
<<"Response: HTTP "/utf8,
(erlang:integer_to_binary(erlang:element(2, Resp)))/binary>>
),
map_response(Resp)
end
)
end
).
-file("src/pig/ai/http.gleam", 60).
?DOC(
" Send a POST request with the default 120-second timeout.\n"
" Returns response body or AiError.\n"
).
-spec post(binary(), list({binary(), binary()}), binary()) -> {ok, binary()} |
{error, pig@ai@error:ai_error()}.
post(Url, Headers, Body) ->
post_with_timeout(Url, Headers, Body, 120000).