-module(formz).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([create_form/1, decode/1, items/1, definition/3, definition_with_custom_optional/5, verify/2, widget/2, get_parse/1, get_optional_parse/1, named/1, set_name/2, set_label/2, set_help_text/2, set_disabled/2, make_disabled/1, limited_list/4, list/3, simple_limit_check/3, limit_at_least/1, limit_at_most/1, limit_between/2, data/2, field/3, required_field/3, subform/3, decode_then_try/2, get/2, validate/2, validate_all/1, update/3, update_config/3, field_error/3, listfield_errors/3, get_states/1]).
-export_type([form/2, item/1, input_state/0, requirement/0, config/0, definition/3, list_parsing_result/1]).
-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(
" A form is a list of fields and a decoder function. This module uses a\n"
" series of callbacks to construct the decoder function as the fields are\n"
" being added to the form. The idea is that you'd have a function that\n"
" makes the form using the `use` syntax, and then be able to use the form\n"
" later for parsing or rendering in different contexts.\n"
"\n"
" You can use this cheatsheet to navigate the module documentation:\n"
"\n"
"
\n"
"\n"
" ### Examples\n"
"\n"
" ```gleam\n"
" fn make_form() {\n"
" use name <- formz.required_field(field(\"name\"), definitions.text_field())\n"
"\n"
" formz.create_form(name)\n"
" }\n"
"\n"
" fn process_form() {\n"
" make_form()\n"
" |> formz.data([#(\"name\", \"Louis\"))])\n"
" |> formz.decode\n"
" # -> Ok(\"Louis\")\n"
" }\n"
" ```\n"
"\n"
" ```gleam\n"
" fn make_form() {\n"
" use greeting <- field(field(\"greeting\"), definitions.text_field())\n"
" use name <- field(field(\"name\"), definitions.text_field())\n"
"\n"
" formz.create_form(greeting <> \" \" <> name)\n"
" }\n"
"\n"
" fn process_form() {\n"
" make_form()\n"
" |> data([#(\"greeting\", \"Hello\"), #(\"name\", \"World\")])\n"
" |> formz.decode\n"
" # -> Ok(\"Hello World\")\n"
" }\n"
" ```\n"
).
-opaque form(FYC, FYD) :: {form,
list(item(FYC)),
fun((list(item(FYC))) -> {ok, FYD} | {error, list(item(FYC))}),
FYD}.
-type item(FYE) :: {field, config(), input_state(), FYE} |
{list_field,
config(),
list(input_state()),
fun((integer()) -> {ok, list(input_state())} | {error, integer()}),
FYE} |
{sub_form, config(), list(item(FYE))}.
-type input_state() :: {unvalidated, binary(), requirement()} |
{valid, binary(), requirement()} |
{invalid, binary(), requirement(), binary()}.
-type requirement() :: optional | required.
-type config() :: {config, binary(), binary(), binary(), boolean()}.
-opaque definition(FYF, FYG, FYH) :: {definition,
FYF,
fun((binary()) -> {ok, FYG} | {error, binary()}),
FYG,
fun((fun((binary()) -> {ok, FYG} | {error, binary()}), binary()) -> {ok,
FYH} |
{error, binary()}),
FYH}.
-type list_parsing_result(FYI) :: {list_parsing_result,
binary(),
requirement(),
{ok, FYI} | {error, binary()}}.
-file("src/formz.gleam", 323).
?DOC(
" Create an empty form that \"decodes\" directly to `thing`. This is\n"
" intended to be the final return value of a chain of callbacks that adds\n"
" the form's fields.\n"
"\n"
" ```gleam\n"
" create_form(1)\n"
" |> decode\n"
" # -> Ok(1)\n"
" ```\n"
" ```gleam\n"
" fn make_form() {\n"
" use field1 <- formz.required_field(field(\"field1\"), definitions.text_field())\n"
" use field2 <- formz.required_field(field(\"field2\"), definitions.text_field())\n"
" use field3 <- formz.required_field(field(\"field3\"), definitions.text_field())\n"
"\n"
" formz.create_form(#(field1, field2, field3))\n"
" }\n"
" ```\n"
).
-spec create_form(FYM) -> form(any(), FYM).
create_form(Thing) ->
{form, [], fun(_) -> {ok, Thing} end, Thing}.
-file("src/formz.gleam", 618).
-spec state_from_parse_result(list_parsing_result(any())) -> input_state().
state_from_parse_result(Result) ->
{list_parsing_result, Value, Requirement, Output} = Result,
case Output of
{ok, _} ->
{valid, Value, Requirement};
{error, Error} ->
{invalid, Value, Requirement, Error}
end.
-file("src/formz.gleam", 639).
-spec parse_list_state(
input_state(),
fun((binary()) -> {ok, GAS} | {error, binary()}),
GAS
) -> list_parsing_result(GAS).
parse_list_state(State, Parse, Stub) ->
_pipe = case {erlang:element(2, State), erlang:element(3, State)} of
{<<""/utf8>>, optional} ->
{ok, Stub};
{_, _} ->
Parse(erlang:element(2, State))
end,
{list_parsing_result,
erlang:element(2, State),
erlang:element(3, State),
_pipe}.
-file("src/formz.gleam", 651).
-spec mark_above_max_values_as_invalid(
list(list_parsing_result(GAW)),
fun((integer()) -> {ok, list(input_state())} | {error, integer()})
) -> list(list_parsing_result(GAW)).
mark_above_max_values_as_invalid(List, Limit_check) ->
Count = gleam@list:fold(
List,
0,
fun(Acc, R) -> case {erlang:element(2, R), erlang:element(3, R)} of
{<<""/utf8>>, optional} ->
Acc;
{_, _} ->
Acc + 1
end end
),
case Limit_check(Count) of
{ok, _} ->
List;
{error, Num_too_many} ->
Max_index = Count - Num_too_many,
{Mapped_results, _} = gleam@list:fold(
List,
{[], 0},
fun(Acc@1, R@1) ->
{Results, I} = Acc@1,
case {erlang:element(2, R@1),
erlang:element(3, R@1),
I >= Max_index} of
{<<""/utf8>>, optional, _} ->
{[R@1 | Results], I};
{_, _, false} ->
{[R@1 | Results], I + 1};
{V, R@2, true} ->
{[{list_parsing_result,
V,
R@2,
{error,
<<"exceeds maximum allowed items"/utf8>>}} |
Results],
I + 1}
end
end
),
_pipe = Mapped_results,
lists:reverse(_pipe)
end.
-file("src/formz.gleam", 861).
?DOC(
" Decode the form. This means step through the fields one by one, parsing\n"
" them individually. If any field fails to parse, the whole form is considered\n"
" invalid, however it will still continue parsing the rest of the fields to\n"
" collect all errors. This is useful for showing all errors at once. If no\n"
" fields fail to parse, the decoded value is returned, which is the value given\n"
" to `create_form`.\n"
"\n"
" If you'd like to decode the form but not get the output, so you can give\n"
" feedback to a user in response to input, you can use `validate` or `validate_all`.\n"
).
-spec decode(form(GCS, GCT)) -> {ok, GCT} | {error, form(GCS, GCT)}.
decode(Form) ->
case (erlang:element(3, Form))(erlang:element(2, Form)) of
{ok, Output} ->
{ok, Output};
{error, Items} ->
{error,
begin
_record = Form,
{form,
Items,
erlang:element(3, _record),
erlang:element(4, _record)}
end}
end.
-file("src/formz.gleam", 980).
?DOC(
" Get each [`Item`](https://hexdocs.pm/formz/formz.html#Item) added\n"
" to the form. Any time a field, list field, or subform are added, a `Item`\n"
" is created. Use this to loop through all the fields of your form and\n"
" generate HTML for them.\n"
).
-spec items(form(GEH, any())) -> list(item(GEH)).
items(Form) ->
erlang:element(2, Form).
-file("src/formz.gleam", 1107).
-spec get_item_name(item(any())) -> binary().
get_item_name(Item) ->
case Item of
{field, Field, _, _} ->
erlang:element(2, Field);
{list_field, Field@1, _, _, _} ->
erlang:element(2, Field@1);
{sub_form, Subform, _} ->
erlang:element(2, Subform)
end.
-file("src/formz.gleam", 1118).
?DOC(
" Create a simple `Definition` that is parsed as an `Option` if the field\n"
" is empty. See [formz_string](https://hexdocs.pm/formz_string/formz_string/definitions.html)\n"
" for more examples of making widgets and definitions.\n"
).
-spec definition(GGF, fun((binary()) -> {ok, GGG} | {error, binary()}), GGG) -> definition(GGF, GGG, gleam@option:option(GGG)).
definition(Widget, Parse, Stub) ->
{definition, Widget, Parse, Stub, fun(Fun, Str) -> case Str of
<<""/utf8>> ->
{ok, none};
_ ->
_pipe = Fun(Str),
gleam@result:map(_pipe, fun(Field@0) -> {some, Field@0} end)
end end, none}.
-file("src/formz.gleam", 1147).
?DOC(
" Create a `Definition` that can parse to any type if the field is optional.\n"
" This takes two functions. The first, `parse`, is the \"required\" parse\n"
" function, which takes the raw string value, and turns it into the required\n"
" type. The second, `optional_parse`, is a function that takes the normal\n"
" parse function and the raw string value, and it is supposed to check the\n"
" input string: if it is empty, return an `Ok` with a value of the optional\n"
" type; and if it's not empty use the normal parse function.\n"
"\n"
" See [formz_string](https://hexdocs.pm/formz_string/formz_string/definitions.html)\n"
" for more examples of making widgets and definitions.\n"
).
-spec definition_with_custom_optional(
GGK,
fun((binary()) -> {ok, GGL} | {error, binary()}),
GGL,
fun((fun((binary()) -> {ok, GGL} | {error, binary()}), binary()) -> {ok,
GGQ} |
{error, binary()}),
GGQ
) -> definition(GGK, GGL, GGQ).
definition_with_custom_optional(
Widget,
Parse,
Stub,
Optional_parse,
Optional_stub
) ->
{definition, Widget, Parse, Stub, Optional_parse, Optional_stub}.
-file("src/formz.gleam", 1176).
?DOC(
" Chain additional validation onto the `parse` function. This is\n"
" useful if you don't need to change the returned type, but might have\n"
" additional constraints. Like say, requiring a `String` to be at least\n"
" a certain length, or that an Int must be positive.\n"
"\n"
" ### Example\n"
" ```gleam\n"
" field\n"
" |> validate(fn(i) {\n"
" case i > 0 {\n"
" True -> Ok(i)\n"
" False -> Error(\"must be positive\")\n"
" }\n"
" }),\n"
" ```\n"
).
-spec verify(
definition(GGW, GGX, GGY),
fun((GGX) -> {ok, GGX} | {error, binary()})
) -> definition(GGW, GGX, GGY).
verify(Def, Fun) ->
_record = Def,
{definition, erlang:element(2, _record), fun(Val) -> _pipe = Val,
_pipe@1 = (erlang:element(3, Def))(_pipe),
gleam@result:'try'(_pipe@1, Fun) end, erlang:element(4, _record), erlang:element(
5,
_record
), erlang:element(6, _record)}.
-file("src/formz.gleam", 1222).
?DOC(" Update the widget of a definition.\n").
-spec widget(definition(GHH, GHI, GHJ), GHH) -> definition(GHH, GHI, GHJ).
widget(Def, Widget) ->
_record = Def,
{definition,
Widget,
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record)}.
-file("src/formz.gleam", 1230).
?DOC(false).
-spec get_parse(definition(any(), GHR, any())) -> fun((binary()) -> {ok, GHR} |
{error, binary()}).
get_parse(Def) ->
erlang:element(3, Def).
-file("src/formz.gleam", 1237).
?DOC(false).
-spec get_optional_parse(definition(any(), GHZ, GIA)) -> fun((fun((binary()) -> {ok,
GHZ} |
{error, binary()}), binary()) -> {ok, GIA} | {error, binary()}).
get_optional_parse(Def) ->
erlang:element(5, Def).
-file("src/formz.gleam", 1271).
?DOC(
" Create a field with the given name.\n"
"\n"
" It uses\n"
" [justin.sentence_case](https://hexdocs.pm/justin/justin.html#sentence_case)\n"
" to create an initial label. You can override the label with the `set_label`\n"
" function. I don't know if this is very english-centric, so let me know if\n"
" this is a bad experience in other languages and I'll consider\n"
" something else.\n"
"\n"
" ```gleam\n"
" field(\"name\")\n"
" |> set_label(\"Full Name\")\n"
" ```\n"
).
-spec named(binary()) -> config().
named(Name) ->
{config, Name, justin:sentence_case(Name), <<""/utf8>>, false}.
-file("src/formz.gleam", 1281).
?DOC(" Set the name of the field. This is the key that will be used for the data.\n").
-spec set_name(config(), binary()) -> config().
set_name(Config, Name) ->
_record = Config,
{config,
Name,
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/formz.gleam", 708).
-spec add_prefix_to_item(item(GBN), binary()) -> item(GBN).
add_prefix_to_item(Item, Prefix) ->
case Item of
{field, Item_details, State, Widget} ->
New_name = <<<>/binary,
(erlang:element(2, Item_details))/binary>>,
{field,
begin
_pipe = Item_details,
set_name(_pipe, New_name)
end,
State,
Widget};
{list_field, Item_details@1, States, Limit_check, Widget@1} ->
New_name@1 = <<<>/binary,
(erlang:element(2, Item_details@1))/binary>>,
{list_field,
begin
_pipe@1 = Item_details@1,
set_name(_pipe@1, New_name@1)
end,
States,
Limit_check,
Widget@1};
{sub_form, Item_details@2, Sub_items} ->
New_name@2 = <<<>/binary,
(erlang:element(2, Item_details@2))/binary>>,
{sub_form,
begin
_pipe@2 = Item_details@2,
set_name(_pipe@2, New_name@2)
end,
Sub_items}
end.
-file("src/formz.gleam", 1287).
?DOC(
" Set the label of the field. This is the primary text that will be displayed\n"
" to the user about the field.\n"
).
-spec set_label(config(), binary()) -> config().
set_label(Config, Label) ->
_record = Config,
{config,
erlang:element(2, _record),
Label,
erlang:element(4, _record),
erlang:element(5, _record)}.
-file("src/formz.gleam", 1292).
?DOC(" Additional instructions or help text to display to the user about the field.\n").
-spec set_help_text(config(), binary()) -> config().
set_help_text(Config, Help_text) ->
_record = Config,
{config,
erlang:element(2, _record),
erlang:element(3, _record),
Help_text,
erlang:element(5, _record)}.
-file("src/formz.gleam", 1297).
?DOC(" Set the `disabled` flag directly.\n").
-spec set_disabled(config(), boolean()) -> config().
set_disabled(Config, Disabled) ->
_record = Config,
{config,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
Disabled}.
-file("src/formz.gleam", 1306).
?DOC(
" Mark the form `Item` as disabled. This will prevent the user from\n"
" interacting with the field. If you do this for a subform, it will\n"
" only work if the form generator renders the subform as a `fieldset`.\n"
" For example, HTML does not allow you to mark inputs in a `
`\n"
" disabled as group but you can do this with a `