-module(lightspeed@form).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/form.gleam").
-export([empty/0, from_entries/1, parse_payload/1, fields/1, to_entries/1, value/2, values/2, require/2, int/2, bool/2, checked/2, put/3, error_to_string/1]).
-export_type([field/0, form_data/0, form_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.
?MODULEDOC(" Form binding helpers for event payloads and typed form values.\n").
-type field() :: {field, binary(), binary()}.
-type form_data() :: {form_data, list(field())}.
-type form_error() :: {missing_field, binary()} |
{invalid_integer, binary(), binary()} |
{invalid_boolean, binary(), binary()}.
-file("src/lightspeed/form.gleam", 26).
?DOC(" Empty form data.\n").
-spec empty() -> form_data().
empty() ->
{form_data, []}.
-file("src/lightspeed/form.gleam", 130).
-spec from_entries_loop(list({binary(), binary()}), list(field())) -> list(field()).
from_entries_loop(Entries, Fields_rev) ->
case Entries of
[] ->
lists:reverse(Fields_rev);
[{Name, Value} | Rest] ->
from_entries_loop(Rest, [{field, Name, Value} | Fields_rev])
end.
-file("src/lightspeed/form.gleam", 31).
?DOC(" Build form data from key/value entries.\n").
-spec from_entries(list({binary(), binary()})) -> form_data().
from_entries(Entries) ->
{form_data, from_entries_loop(Entries, [])}.
-file("src/lightspeed/form.gleam", 183).
-spec normalize_component(binary()) -> binary().
normalize_component(Value) ->
_pipe = Value,
gleam@string:replace(_pipe, <<"+"/utf8>>, <<" "/utf8>>).
-file("src/lightspeed/form.gleam", 175).
-spec join_with_equals(list(binary())) -> binary().
join_with_equals(Parts) ->
case Parts of
[] ->
<<""/utf8>>;
[Part] ->
Part;
[Part@1 | Rest] ->
<<<<Part@1/binary, "="/utf8>>/binary,
(join_with_equals(Rest))/binary>>
end.
-file("src/lightspeed/form.gleam", 166).
-spec split_pair(binary()) -> {binary(), binary()}.
split_pair(Pair) ->
case gleam@string:split(Pair, <<"="/utf8>>) of
[] ->
{<<""/utf8>>, <<""/utf8>>};
[Name] ->
{Name, <<""/utf8>>};
[Name@1, Value] ->
{Name@1, Value};
[Name@2, Value@1 | Rest] ->
{Name@2,
<<<<Value@1/binary, "="/utf8>>/binary,
(join_with_equals(Rest))/binary>>}
end.
-file("src/lightspeed/form.gleam", 240).
-spec concat(list(HTW), list(HTW)) -> list(HTW).
concat(Left, Right) ->
case Left of
[] ->
Right;
[Entry | Rest] ->
[Entry | concat(Rest, Right)]
end.
-file("src/lightspeed/form.gleam", 141).
-spec parse_pairs(list(binary()), list(field()), list(field())) -> list(field()).
parse_pairs(Pairs, Fields_rev, Buffer) ->
case Pairs of
[] ->
lists:reverse(concat(Buffer, Fields_rev));
[Pair | Rest] ->
case split_pair(Pair) of
{Name, Value} ->
parse_pairs(
Rest,
[{field,
normalize_component(Name),
normalize_component(Value)} |
Fields_rev],
Buffer
)
end
end.
-file("src/lightspeed/form.gleam", 39).
?DOC(
" Parse a URL-form-encoded style payload (`name=value&next=...`).\n"
"\n"
" This parser keeps values as plain strings and does not perform percent\n"
" decoding. It is deterministic and suitable for typed server decoders.\n"
).
-spec parse_payload(binary()) -> form_data().
parse_payload(Payload) ->
case Payload of
<<""/utf8>> ->
empty();
_ ->
_pipe = Payload,
_pipe@1 = gleam@string:split(_pipe, <<"&"/utf8>>),
_pipe@2 = parse_pairs(_pipe@1, [], []),
{form_data, _pipe@2}
end.
-file("src/lightspeed/form.gleam", 51).
?DOC(" Return all fields.\n").
-spec fields(form_data()) -> list(field()).
fields(Form) ->
erlang:element(2, Form).
-file("src/lightspeed/form.gleam", 188).
-spec to_entries_loop(list(field()), list({binary(), binary()})) -> list({binary(),
binary()}).
to_entries_loop(Fields, Entries_rev) ->
case Fields of
[] ->
lists:reverse(Entries_rev);
[{field, Name, Value} | Rest] ->
to_entries_loop(Rest, [{Name, Value} | Entries_rev])
end.
-file("src/lightspeed/form.gleam", 56).
?DOC(" Convert form data back into entries.\n").
-spec to_entries(form_data()) -> list({binary(), binary()}).
to_entries(Form) ->
to_entries_loop(erlang:element(2, Form), []).
-file("src/lightspeed/form.gleam", 199).
-spec find_first(list(field()), binary()) -> gleam@option:option(binary()).
find_first(Fields, Name) ->
case Fields of
[] ->
none;
[{field, Field_name, Field_value} | Rest] ->
case Field_name =:= Name of
true ->
{some, Field_value};
false ->
find_first(Rest, Name)
end
end.
-file("src/lightspeed/form.gleam", 61).
?DOC(" Return first value for a field.\n").
-spec value(form_data(), binary()) -> gleam@option:option(binary()).
value(Form, Name) ->
find_first(erlang:element(2, Form), Name).
-file("src/lightspeed/form.gleam", 210).
-spec find_all(list(field()), binary(), list(binary())) -> list(binary()).
find_all(Fields, Name, Values_rev) ->
case Fields of
[] ->
lists:reverse(Values_rev);
[{field, Field_name, Field_value} | Rest] ->
case Field_name =:= Name of
true ->
find_all(Rest, Name, [Field_value | Values_rev]);
false ->
find_all(Rest, Name, Values_rev)
end
end.
-file("src/lightspeed/form.gleam", 66).
?DOC(" Return all values for a field.\n").
-spec values(form_data(), binary()) -> list(binary()).
values(Form, Name) ->
find_all(erlang:element(2, Form), Name, []).
-file("src/lightspeed/form.gleam", 71).
?DOC(" Return a required field or an explicit missing-field error.\n").
-spec require(form_data(), binary()) -> {ok, binary()} | {error, form_error()}.
require(Form, Name) ->
case value(Form, Name) of
{some, Field_value} ->
{ok, Field_value};
none ->
{error, {missing_field, Name}}
end.
-file("src/lightspeed/form.gleam", 79).
?DOC(" Parse a required integer field.\n").
-spec int(form_data(), binary()) -> {ok, integer()} | {error, form_error()}.
int(Form, Name) ->
case require(Form, Name) of
{error, Error} ->
{error, Error};
{ok, Field_value} ->
case gleam_stdlib:parse_int(Field_value) of
{ok, Parsed} ->
{ok, Parsed};
{error, _} ->
{error, {invalid_integer, Name, Field_value}}
end
end.
-file("src/lightspeed/form.gleam", 91).
?DOC(" Parse a required boolean field.\n").
-spec bool(form_data(), binary()) -> {ok, boolean()} | {error, form_error()}.
bool(Form, Name) ->
case require(Form, Name) of
{error, Error} ->
{error, Error};
{ok, Field_value} ->
case Field_value of
<<"true"/utf8>> ->
{ok, true};
<<"false"/utf8>> ->
{ok, false};
<<"1"/utf8>> ->
{ok, true};
<<"0"/utf8>> ->
{ok, false};
<<"on"/utf8>> ->
{ok, true};
<<"off"/utf8>> ->
{ok, false};
_ ->
{error, {invalid_boolean, Name, Field_value}}
end
end.
-file("src/lightspeed/form.gleam", 108).
?DOC(" Presence helper for checkbox-style fields.\n").
-spec checked(form_data(), binary()) -> boolean().
checked(Form, Name) ->
case value(Form, Name) of
{some, _} ->
true;
none ->
false
end.
-file("src/lightspeed/form.gleam", 225).
-spec remove_name(list(field()), binary(), list(field())) -> list(field()).
remove_name(Fields, Name, Kept_rev) ->
case Fields of
[] ->
lists:reverse(Kept_rev);
[Field | Rest] ->
case erlang:element(2, Field) =:= Name of
true ->
remove_name(Rest, Name, Kept_rev);
false ->
remove_name(Rest, Name, [Field | Kept_rev])
end
end.
-file("src/lightspeed/form.gleam", 116).
?DOC(" Upsert one form field value.\n").
-spec put(form_data(), binary(), binary()) -> form_data().
put(Form, Name, Value) ->
Remaining = remove_name(erlang:element(2, Form), Name, []),
{form_data, [{field, Name, Value} | Remaining]}.
-file("src/lightspeed/form.gleam", 122).
?DOC(" Render stable error string for logs or telemetry.\n").
-spec error_to_string(form_error()) -> binary().
error_to_string(Error) ->
case Error of
{missing_field, Name} ->
<<"missing_field:"/utf8, Name/binary>>;
{invalid_integer, Name@1, Value} ->
<<<<<<"invalid_integer:"/utf8, Name@1/binary>>/binary, ":"/utf8>>/binary,
Value/binary>>;
{invalid_boolean, Name@2, Value@1} ->
<<<<<<"invalid_boolean:"/utf8, Name@2/binary>>/binary, ":"/utf8>>/binary,
Value@1/binary>>
end.