src/datastar_gleam@wisp.erl

-module(datastar_gleam@wisp).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/datastar_gleam/wisp.gleam").
-export([is_datastar_request/1, read_signals/2]).
-export_type([read_signals_error/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.

-type read_signals_error() :: missing_header |
    invalid_query |
    invalid_body |
    {json_decode_error, gleam@json:decode_error()}.

-file("src/datastar_gleam/wisp.gleam", 19).
?DOC(
    " Check whether a Wisp request was initiated by Datastar.\n"
    "\n"
    " This inspects the `datastar-request` header that the Datastar frontend\n"
    " automatically adds to every outgoing request.\n"
).
-spec is_datastar_request(
    gleam@http@request:request(wisp@internal:connection())
) -> boolean().
is_datastar_request(Req) ->
    case gleam@http@request:get_header(Req, <<"datastar-request"/utf8>>) of
        {ok, _} ->
            true;

        {error, _} ->
            false
    end.

-file("src/datastar_gleam/wisp.gleam", 83).
-spec read_signals_from_body(
    gleam@http@request:request(wisp@internal:connection()),
    gleam@dynamic@decode:decoder(ABDI)
) -> {ok, ABDI} | {error, read_signals_error()}.
read_signals_from_body(Req, Decoder) ->
    case wisp:read_body_bits(Req) of
        {error, _} ->
            {error, invalid_body};

        {ok, Bits} ->
            case gleam@bit_array:to_string(Bits) of
                {error, nil} ->
                    {error, invalid_body};

                {ok, Body} ->
                    case gleam@json:parse(Body, Decoder) of
                        {ok, Val} ->
                            {ok, Val};

                        {error, Err} ->
                            {error, {json_decode_error, Err}}
                    end
            end
    end.

-file("src/datastar_gleam/wisp.gleam", 67).
-spec read_signals_from_query(
    gleam@http@request:request(wisp@internal:connection()),
    gleam@dynamic@decode:decoder(ABDE)
) -> {ok, ABDE} | {error, read_signals_error()}.
read_signals_from_query(Req, Decoder) ->
    Query = wisp:get_query(Req),
    case gleam@list:key_find(Query, <<"datastar"/utf8>>) of
        {error, nil} ->
            {error, invalid_query};

        {ok, Raw} ->
            case gleam@json:parse(Raw, Decoder) of
                {ok, Val} ->
                    {ok, Val};

                {error, Err} ->
                    {error, {json_decode_error, Err}}
            end
    end.

-file("src/datastar_gleam/wisp.gleam", 52).
?DOC(
    " Read Datastar signals from a Wisp request.\n"
    "\n"
    " For **GET** requests, expects `?datastar=url_encoded_json`.\n"
    " For **POST/PUT/PATCH** requests, expects a JSON body.\n"
    "\n"
    " ```gleam\n"
    " import gleam/dynamic/decode\n"
    "\n"
    " let decoder = decode.at([\"delay\"], decode.int)\n"
    " case datastar_wisp.read_signals(req, decoder) {\n"
    "   Ok(signals) -> // use signals\n"
    "   Error(datastar_wisp.MissingHeader) -> // not a datastar request\n"
    " }\n"
    " ```\n"
).
-spec read_signals(
    gleam@http@request:request(wisp@internal:connection()),
    gleam@dynamic@decode:decoder(ABDA)
) -> {ok, ABDA} | {error, read_signals_error()}.
read_signals(Req, Decoder) ->
    case is_datastar_request(Req) of
        false ->
            {error, missing_header};

        true ->
            case erlang:element(2, Req) of
                get ->
                    read_signals_from_query(Req, Decoder);

                _ ->
                    read_signals_from_body(Req, Decoder)
            end
    end.