···66// - list fields
77// - form sets
88// - csrf token
99-// - required/option
1091111-import forma/field.{type Field, Field}
1212-import forma/input.{type Input}
1010+import forma/field.{type Field}
1111+import forma/input.{type Input, Input}
1312import gleam/list
1413import gleam/option.{type Option, None, Some}
1514import gleam/result
···18171918pub type NoDecoder
20192121-pub type FieldsWithErrors(format) =
2222- List(Field(format))
2323-2420pub opaque type Form(format, output, decoder, has_decoder) {
2521 Form(
2626- fields: List(Field(format)),
2727- parse_with: fn(List(Field(format)), decoder) ->
2828- Result(output, FieldsWithErrors(format)),
2222+ fields: List(Input(format)),
2323+ parse_with: fn(List(Input(format)), decoder) ->
2424+ Result(output, List(Input(format))),
2925 decoder: Option(decoder),
3026 )
3127}
···4137 form_output,
4238 has_decoder,
4339 ),
4444- definition: Input(format, decoder_step_input),
4040+ definition: Field(format, decoder_step_input),
4541) -> Form(format, decoder_step_output, form_output, has_decoder) {
4646- let Form(fields, parse_with, decoder) = form
4242+ let Form(inputs, parse_with, decoder) = form
47434844 // create new form with the new field and update the parse
4945 // function to handle the new details from the type of the
5046 // field
5147 Form(
5252- fields: [definition.field, ..fields],
5353- parse_with: fn(fields, decoder: form_output) {
4848+ fields: [definition.input, ..inputs],
4949+ parse_with: fn(inputs, decoder: form_output) {
5450 // can do let assert because we know there's at least one field since
5551 // we just added one
5656- let assert [field, ..rest] = fields
5757- case parse_with(rest, decoder), definition.transform(field.value) {
5252+ let assert [input, ..rest] = inputs
5353+ case parse_with(rest, decoder), definition.transform(input.value) {
5854 // the form we've already parsed has no errors and the field
5955 // we just parsed has no errors. so we can move on to the next
6056 Ok(next), Ok(value) -> Ok(next(value))
61576258 // the form already has errors even though this one succeeded.
6359 // so add this to the list and stop anymore parsing
6464- Error(fields), Ok(_value) -> Error([field, ..fields])
6060+ Error(inputs), Ok(_value) -> Error([input, ..inputs])
65616662 // form was good so far, but this field errored, so need to
6763 // mark this field as invalid and return all the fields we've got
6864 // so far
6969- Ok(_), Error(error) -> Error([field.set_error(field, error), ..rest])
6565+ Ok(_), Error(error) -> Error([input.set_error(input, error), ..rest])
70667167 // form already has errors and this field errored, so add this field
7268 // to the list
7369 Error(fields), Error(error) ->
7474- Error([field.set_error(field, error), ..fields])
7070+ Error([input.set_error(input, error), ..fields])
7571 }
7672 },
7773 decoder:,
···80768177pub fn data(
8278 form: Form(a, b, format, has_decoder),
8383- input: List(#(String, String)),
7979+ field: List(#(String, String)),
8480) -> Form(a, b, format, has_decoder) {
8581 case form {
8682 Form(fields, parse_with, decoder) -> {
···8884 // we always prepend fields, so reverse to get correct order
8985 // TODO I think we're going to make it so order doesn't matter
9086 |> list.reverse
9191- |> do_add_input_data(input, [])
8787+ |> do_add_input_data(field, [])
9288 |> Form(parse_with, decoder)
9389 }
9490 // FormWithErrors(..) -> form
···9692}
97939894fn do_add_input_data(
9999- fields: List(Field(format)),
9595+ fields: List(Input(format)),
10096 data: List(#(String, String)),
101101- acc: List(Field(format)),
9797+ acc: List(Input(format)),
10298) {
10399 case fields, data {
104100 // no more fields, we've return all the fields with data we have accumulated
···106102 // no more data! return all the fields we have left plus the ones we accumulated
107103 _, [] -> list.append(fields, acc)
108104 // we have a field and data, and the names match. update field to have data
109109- [Field(name: field_name, ..) as field, ..fields_rest],
105105+ [Input(name: field_name, ..) as field, ..fields_rest],
110106 [#(data_name, value), ..data_rest]
111107 if field_name == data_name
112108 ->
113109 do_add_input_data(fields_rest, data_rest, [
114114- field.set_value(field, value),
110110+ input.set_value(field, value),
115111 ..acc
116112 ])
117113 // at this point we still have fields and data left, but the first
···151147 parse(form) |> result.try(fun(_, form))
152148}
153149154154-pub fn get_fields(form: Form(format, a, b, has_decoder)) -> List(Field(format)) {
150150+pub fn get_inputs(form: Form(format, a, b, has_decoder)) -> List(Input(format)) {
155151 form.fields |> list.reverse
156152}
157153158158-pub fn set_field_error(
154154+pub fn update_input(
159155 form: Form(format, output, decoder, has_decoder),
160156 name: String,
161161- error: String,
162162-) -> Form(format, output, decoder, has_decoder) {
163163- let updated =
164164- form.fields
165165- |> list.map(fn(field) {
166166- case field.name == name {
167167- True -> field.set_error(field, error)
168168- False -> field
169169- }
170170- })
171171- Form(updated, form.parse_with, form.decoder)
172172-}
173173-174174-pub fn field_update(
175175- form: Form(format, output, decoder, has_decoder),
176176- name: String,
177177- fun: fn(Field(format)) -> Field(format),
157157+ fun: fn(Input(format)) -> Input(format),
178158) -> Form(format, output, decoder, has_decoder) {
179159 form.fields
180160 |> list.map(fn(field) {
+49-25
forma/src/forma/forma_use/forma.gleam
···11/////// field related functions
2233-import forma/field.{type Field, Field}
44-import forma/input.{type Input}
33+import forma/field.{type Field}
44+import forma/input.{type Input, Input}
55import gleam/dict
66import gleam/list
77import gleam/result
8899pub opaque type Form(format, output) {
1010 Form(
1111- fields: List(Field(format)),
1212- parse: fn(List(Field(format))) -> Result(output, List(Field(format))),
1111+ inputs: List(Input(format)),
1212+ parse: fn(List(Input(format))) -> Result(output, List(Input(format))),
1313 )
1414}
15151616pub fn with(
1717- definition: Input(format, input_output),
1717+ field: Field(format, input_output),
1818 fun: fn(input_output) -> Form(format, form_output),
1919) -> Form(format, form_output) {
2020- let field = definition.field
2121- let next = fun(definition.default)
2222- Form([field, ..next.fields], parse: fn(fields) {
2323- // pull out the latest version of this field to get latest data
2424- let assert [field, ..next_fields] = fields
2020+ // we pass in our default value, and we're going to throw away the
2121+ // decoded result here, we just care about pulling out the fields
2222+ // from the form.
2323+ let next = fun(field.default)
2424+2525+ // prepend the new input to the inputs from the form we got in the
2626+ // previous step.
2727+ let updated_inputs = [field.input, ..next.inputs]
25282626- let input_output = definition.transform(field.value)
2929+ // now create the parse function. parse function accepts most recent
3030+ // version of input list, since data can be added to it. the list
3131+ // above we just needed for the initial setup.
3232+ let parse = fn(inputs: List(Input(format))) {
3333+ // pull out the latest version of this field to get latest input data
3434+ let assert [input, ..next_inputs] = inputs
27352828- let next_form = fun(input_output |> result.unwrap(definition.default))
2929- let form_output = next_form.parse(next_fields)
3636+ // transform the input data using the transform/validate/decode/etc function
3737+ let input_output = field.transform(input.value)
30383939+ // pass our transformed input data to the next function/form. if
4040+ // we errored we still do this with our default so we can continue
4141+ // processing all the fields in the form. we will return a form
4242+ // with an error, so if we're on the error track we'll throw away
4343+ // the "output" made with this and just keep the errors.
4444+ let next_form = fun(input_output |> result.unwrap(field.default))
4545+ let form_output = next_form.parse(next_inputs)
4646+4747+ // ok, check which track we're on
3148 case form_output, input_output {
3249 // everything is good! pass along the output
3350 Ok(_), Ok(_) -> form_output
34513552 // form has errors, but this field was good, so add it to the list
3653 // of fields as is.
3737- Error(fields), Ok(_value) -> Error([field, ..fields])
5454+ Error(inputs), Ok(_value) -> Error([input, ..inputs])
38553956 // form was good so far, but this field errored, so need to
4057 // mark this field as invalid and return all the fields we've got
4158 // so far
4259 Ok(_), Error(error) ->
4343- field |> field.set_error(error) |> list.prepend(next_fields, _) |> Error
6060+ input
6161+ |> input.set_error(error)
6262+ |> list.prepend(next_inputs, _)
6363+ |> Error
44644565 // form already has errors and this field errored, so add this field
4666 // to the list of errors
4767 Error(fields), Error(error) ->
4848- field |> field.set_error(error) |> list.prepend(fields, _) |> Error
6868+ input
6969+ |> input.set_error(error)
7070+ |> list.prepend(fields, _)
7171+ |> Error
4972 }
5050- })
7373+ }
7474+ Form(updated_inputs, parse: parse)
5175}
52765377pub fn data(
5478 form: Form(format, output),
5555- input: List(#(String, String)),
7979+ input_data: List(#(String, String)),
5680) -> Form(format, output) {
5757- let data = dict.from_list(input)
8181+ let data = dict.from_list(input_data)
5882 let Form(fields, parse) = form
5983 fields
6084 |> list.map(fn(field) {
6185 case dict.get(data, field.name) {
6286 Ok(value) ->
6363- Field(
8787+ Input(
6488 name: field.name,
6589 label: field.label,
6690 help_text: field.help_text,
···94118 parse(form) |> result.try(fun(_, form))
95119}
961209797-pub fn get_fields(form: Form(format, ouput)) -> List(Field(format)) {
9898- form.fields
121121+pub fn get_inputs(form: Form(format, ouput)) -> List(Input(format)) {
122122+ form.inputs
99123}
100124101101-pub fn field_update(
125125+pub fn update_input(
102126 form: Form(format, output),
103127 name: String,
104104- fun: fn(Field(format)) -> Field(format),
128128+ fun: fn(Input(format)) -> Input(format),
105129) -> Form(format, output) {
106106- form.fields
130130+ form.inputs
107131 |> list.map(fn(field) {
108132 case field.name == name {
109133 True -> fun(field)