···11+- form sets
22+- decoders/toy.decoder?
33+- csrf token?
44+- emit warning on duplicate named fields?
55+- clean names so snake_case?
66+- disabled fields
77+- optional fields? (can do optional validation, but that isn't reflected in the html)
88+- errors on hidden fields?
99+- custom types for input.hidden, input.disabled, input.required?
1010+- date fields? https://hexdocs.pm/birl/ https://hexdocs.pm/rada/
···11+import formz/definition.{type Definition}
22+import formz/field
33+import formz/subform
44+import formz/widget
55+66+import gleam/dict
77+import gleam/list
88+import gleam/option.{type Option, None, Some}
99+import gleam/result
1010+1111+pub type HasDecoder
1212+1313+pub type NoDecoder
1414+1515+pub opaque type Form(format, output, decoder, has_decoder) {
1616+ Form(
1717+ items: List(FormItem(format)),
1818+ parse_with: fn(List(FormItem(format)), decoder) ->
1919+ Result(output, List(FormItem(format))),
2020+ decoder: Option(decoder),
2121+ )
2222+}
2323+2424+pub type FormItem(format) {
2525+ Element(field.Field, widget: widget.Widget(format))
2626+ Set(subform.SubForm, items: List(FormItem(format)))
2727+}
2828+2929+pub fn new() -> Form(format, a, a, NoDecoder) {
3030+ Form([], fn(_, output) { Ok(output) }, None)
3131+}
3232+3333+pub fn add(
3434+ previous_form: Form(
3535+ format,
3636+ fn(decoder_step_input) -> decoder_step_output,
3737+ form_output,
3838+ has_decoder,
3939+ ),
4040+ field: field.Field,
4141+ definition: Definition(format, decoder_step_input),
4242+) -> Form(format, decoder_step_output, form_output, has_decoder) {
4343+ let updated_items = [Element(field, definition.widget), ..previous_form.items]
4444+4545+ let parse_with = fn(items, decoder: form_output) {
4646+ // can do let assert because we know there's at least one field since
4747+ // we just added one
4848+ let assert Ok(#(Element(field, widget), rest)) = pop_element(items)
4949+5050+ let input_output = definition.transform(field.value)
5151+5252+ let previous_form_output = previous_form.parse_with(rest, decoder)
5353+ case previous_form_output, input_output {
5454+ // the form we've already parsed has no errors and the field
5555+ // we just parsed has no errors. so we can move on to the next
5656+ Ok(next), Ok(value) -> Ok(next(value))
5757+5858+ // the form already has errors even though this one succeeded.
5959+ // so add this to the list and stop anymore parsing
6060+ Error(items), Ok(_value) -> Error([Element(field, widget), ..items])
6161+6262+ // form was good so far, but this field errored, so need to
6363+ // mark this field as invalid and return all the fields we've got
6464+ // so far
6565+ Ok(_), Error(error) ->
6666+ field
6767+ |> field.set_error(error)
6868+ |> Element(widget)
6969+ |> list.prepend(rest, _)
7070+ |> Error
7171+7272+ // form already has errors and this field errored, so add this field
7373+ // to the list
7474+ Error(items), Error(error) ->
7575+ field
7676+ |> field.set_error(error)
7777+ |> Element(widget)
7878+ |> list.prepend(items, _)
7979+ |> Error
8080+ }
8181+ }
8282+8383+ Form(items: updated_items, parse_with:, decoder: previous_form.decoder)
8484+}
8585+8686+pub fn add_form(
8787+ previous_form: Form(
8888+ format,
8989+ fn(sub_output) -> decoder_step_output,
9090+ form_output,
9191+ has_decoder,
9292+ ),
9393+ subform: subform.SubForm,
9494+ sub: Form(format, sub_output, sub_decoder, HasDecoder),
9595+) -> Form(format, decoder_step_output, form_output, has_decoder) {
9696+ let sub_items =
9797+ sub.items
9898+ |> map_fields(fn(field) {
9999+ field |> field.set_name(subform.name <> "." <> field.name)
100100+ })
101101+102102+ let updated_items = [Set(subform, sub_items), ..previous_form.items]
103103+104104+ let parse_with = fn(items, decoder: form_output) {
105105+ // can do let assert because we know there's at least one field since
106106+ // we just added one
107107+ let assert Ok(#(Set(subform, sub_items), rest)) = pop_element(items)
108108+109109+ let assert Form(_, sub_parse_with, Some(sub_decoder)) = sub
110110+ let form_output = sub_parse_with(sub_items, sub_decoder)
111111+112112+ let previous_form_output = previous_form.parse_with(rest, decoder)
113113+ case previous_form_output, form_output {
114114+ // everything is good! pass along the output
115115+ Ok(next), Ok(value) -> Ok(next(value))
116116+117117+ // form has errors, but this sub form was good, so add it to the list
118118+ // of items as is.
119119+ Error(items), Ok(_value) -> Error([Set(subform, items), ..items])
120120+121121+ // form was good so far, but this sub form errored, so need to
122122+ // hop on error track
123123+ Ok(_), Error(error_fields) -> Error([Set(subform, error_fields), ..rest])
124124+125125+ // form already has errors and this form errored, so add this field
126126+ // to the list of errors
127127+ Error(fields), Error(error_fields) ->
128128+ Error(list.prepend(fields, Set(subform, error_fields)))
129129+ }
130130+ }
131131+ Form(items: updated_items, parse_with:, decoder: previous_form.decoder)
132132+}
133133+134134+fn pop_element(
135135+ items: List(FormItem(format)),
136136+) -> Result(#(FormItem(format), List(FormItem(format))), Nil) {
137137+ case items {
138138+ [] -> Error(Nil)
139139+ [only] -> Ok(#(only, []))
140140+ [Element(..) as item, ..rest] -> Ok(#(item, rest))
141141+ [Set(_, []), ..rest] -> pop_element(rest)
142142+ [Set(s, [first, ..rest_1]), ..rest_2] ->
143143+ pop_element(list.flatten([[first], [Set(s, rest_1)], rest_2]))
144144+ }
145145+}
146146+147147+fn map_fields(
148148+ items: List(FormItem(format)),
149149+ fun: fn(field.Field) -> field.Field,
150150+) -> List(FormItem(format)) {
151151+ list.map(items, fn(item) {
152152+ case item {
153153+ Element(field, widget) -> Element(fun(field), widget)
154154+ Set(s, items) -> Set(s, map_fields(items, fun))
155155+ }
156156+ })
157157+}
158158+159159+pub fn data(
160160+ form: Form(format, output, decoder, has_decoder),
161161+ input_data: List(#(String, String)),
162162+) -> Form(format, output, decoder, has_decoder) {
163163+ let data = dict.from_list(input_data)
164164+ let Form(items, parse, placeholder) = form
165165+ items
166166+ |> map_fields(fn(field) {
167167+ case dict.get(data, field.name) {
168168+ Ok(value) -> field.set_string_value(field, value)
169169+ Error(_) -> field
170170+ }
171171+ })
172172+ |> Form(parse, placeholder)
173173+}
174174+175175+pub fn decodes(
176176+ form: Form(format, output, decoder, has_decoder),
177177+ decoder: decoder,
178178+) -> Form(format, output, decoder, HasDecoder) {
179179+ let Form(fields, parse_with, _) = form
180180+ Form(fields, parse_with, Some(decoder))
181181+}
182182+183183+// pub fn remove_decoder(
184184+// form: Form(format, output1, decoder2, has_decoder),
185185+// ) -> Form(format, decoder2, decoder2, NoDecoder) {
186186+// let Form(fields, parse_with, _) = form
187187+// let parse_with = fn(items: List(FormItem(format)), decoder) {
188188+// parse_with(items, decoder)
189189+// }
190190+// Form(fields, parse_with, None)
191191+// }
192192+193193+pub fn parse(
194194+ form: Form(format, output, decoder, HasDecoder),
195195+) -> Result(output, Form(format, output, decoder, HasDecoder)) {
196196+ // we've tagged that we have a decoder with our has_decoder phantom type
197197+ // so we can get away with let assert here
198198+ let assert Form(items, parse_with, Some(decoder)) = form
199199+ case parse_with(items, decoder) {
200200+ Ok(output) -> Ok(output)
201201+ Error(items) -> Error(Form(items, parse_with, Some(decoder)))
202202+ }
203203+}
204204+205205+pub fn parse_try(
206206+ form: Form(format, output, decoder, HasDecoder),
207207+ apply fun: fn(output, Form(format, output, decoder, HasDecoder)) ->
208208+ Result(c, Form(format, output, decoder, HasDecoder)),
209209+) -> Result(c, Form(format, output, decoder, HasDecoder)) {
210210+ parse(form) |> result.try(fun(_, form))
211211+}
212212+213213+pub fn items(form: Form(format, a, b, has_decoder)) -> List(FormItem(format)) {
214214+ form.items |> list.reverse
215215+}
216216+217217+pub fn get(
218218+ form: Form(format, output, decoder, has_decoder),
219219+ name: String,
220220+) -> Result(FormItem(format), Nil) {
221221+ form.items
222222+ |> list.filter(fn(item) {
223223+ case item {
224224+ Element(i, _) if i.name == name -> True
225225+ Set(s, _) if s.name == name -> True
226226+ _ -> False
227227+ }
228228+ })
229229+ |> list.first
230230+}
231231+232232+pub fn update(
233233+ form: Form(format, output, decoder, has_decoder),
234234+ name: String,
235235+ fun: fn(FormItem(format)) -> FormItem(format),
236236+) {
237237+ form.items
238238+ |> do_formitem_update(name, fun)
239239+ |> Form(form.parse_with, form.decoder)
240240+}
241241+242242+fn do_formitem_update(
243243+ items: List(FormItem(format)),
244244+ name: String,
245245+ fun: fn(FormItem(format)) -> FormItem(format),
246246+) -> List(FormItem(format)) {
247247+ items
248248+ |> list.map(fn(item) {
249249+ case item {
250250+ Element(i, _) if i.name == name -> fun(item)
251251+ Set(s, _) if s.name == name -> fun(item)
252252+ Set(s, items) -> Set(s, do_formitem_update(items, name, fun))
253253+ _ -> item
254254+ }
255255+ })
256256+}
257257+258258+pub fn update_field(
259259+ form: Form(format, output, decoder, has_decoder),
260260+ name: String,
261261+ fun: fn(field.Field) -> field.Field,
262262+) -> Form(format, output, decoder, has_decoder) {
263263+ update(form, name, fn(item) {
264264+ case item {
265265+ Element(field, widget) -> Element(fun(field), widget)
266266+ _ -> item
267267+ }
268268+ })
269269+}
270270+271271+pub fn update_fieldset(
272272+ form: Form(format, output, decoder, has_decoder),
273273+ name: String,
274274+ fun: fn(subform.SubForm) -> subform.SubForm,
275275+) -> Form(format, output, decoder, has_decoder) {
276276+ update(form, name, fn(item) {
277277+ case item {
278278+ Set(subform, items) -> Set(fun(subform), items)
279279+ _ -> item
280280+ }
281281+ })
282282+}
-173
formz/src/formz/formz_pipes.gleam
···11-// https://docs.djangoproject.com/en/5.0/topics/forms/
22-// https://github.com/nakaixo/nakai
33-// date time handling https://hexdocs.pm/birl/index.html
44-55-// TODO
66-// - form sets
77-// - decoders/toy.decoder?
88-// - csrf token
99-// - emit warning on duplicate named fields
1010-// - clean names so snake_case?
1111-// - disabled fields
1212-// - optional fields? (can do optional validation, but that isn't reflected in the html)
1313-// - errors on hidden fields?
1414-// - custom types for input.hidden, input.disabled, input.required?
1515-1616-// import formz/field.{type Field}
1717-// import formz/input.{type Input, Valid}
1818-// import gleam/list
1919-// import gleam/option.{type Option, None, Some}
2020-// import gleam/result
2121-2222-// pub type HasDecoder
2323-2424-// pub type NoDecoder
2525-2626-// pub opaque type Form(format, output, decoder, has_decoder) {
2727-// Form(
2828-// fields: List(Input(format)),
2929-// parse_with: fn(List(Input(format)), decoder) ->
3030-// Result(output, List(Input(format))),
3131-// decoder: Option(decoder),
3232-// )
3333-// }
3434-3535-// pub fn new() -> Form(format, a, a, NoDecoder) {
3636-// Form([], fn(_, output) { Ok(output) }, None)
3737-// }
3838-3939-// pub fn add(
4040-// form: Form(
4141-// format,
4242-// fn(decoder_step_input) -> decoder_step_output,
4343-// form_output,
4444-// has_decoder,
4545-// ),
4646-// definition: Field(format, decoder_step_input),
4747-// ) -> Form(format, decoder_step_output, form_output, has_decoder) {
4848-// let Form(inputs, parse_with, decoder) = form
4949-5050-// // create new form with the new field and update the parse
5151-// // function to handle the new details from the type of the
5252-// // field
5353-// Form(
5454-// fields: [definition.input, ..inputs],
5555-// parse_with: fn(inputs, decoder: form_output) {
5656-// // can do let assert because we know there's at least one field since
5757-// // we just added one
5858-// let assert [input, ..rest] = inputs
5959-// case parse_with(rest, decoder), definition.transform(input.value) {
6060-// // the form we've already parsed has no errors and the field
6161-// // we just parsed has no errors. so we can move on to the next
6262-// Ok(next), Ok(value) -> Ok(next(value))
6363-6464-// // the form already has errors even though this one succeeded.
6565-// // so add this to the list and stop anymore parsing
6666-// Error(inputs), Ok(_value) -> Error([input, ..inputs])
6767-6868-// // form was good so far, but this field errored, so need to
6969-// // mark this field as invalid and return all the fields we've got
7070-// // so far
7171-// Ok(_), Error(error) -> Error([input.set_error(input, error), ..rest])
7272-7373-// // form already has errors and this field errored, so add this field
7474-// // to the list
7575-// Error(fields), Error(error) ->
7676-// Error([input.set_error(input, error), ..fields])
7777-// }
7878-// },
7979-// decoder:,
8080-// )
8181-// }
8282-8383-// pub fn data(
8484-// form: Form(format, a, b, has_decoder),
8585-// field: List(#(String, String)),
8686-// ) -> Form(format, a, b, has_decoder) {
8787-// case form {
8888-// Form(fields, parse_with, decoder) -> {
8989-// fields
9090-// // we always prepend fields, so reverse to get correct order
9191-// // TODO I think we're going to make it so order doesn't matter
9292-// |> list.reverse
9393-// |> do_add_input_data(field, [])
9494-// |> Form(parse_with, decoder)
9595-// }
9696-// // FormWithErrors(..) -> form
9797-// }
9898-// }
9999-100100-// fn do_add_input_data(
101101-// fields: List(Input(format)),
102102-// data: List(#(String, String)),
103103-// acc: List(Input(format)),
104104-// ) {
105105-// case fields, data {
106106-// // no more fields, we've return all the fields with data we have accumulated
107107-// [], _ -> acc
108108-// // no more data! return all the fields we have left plus the ones we accumulated
109109-// _, [] -> list.append(fields, acc)
110110-// // we have a field and data, and the names match. update field to have data
111111-// [Valid(name: field_name, ..) as field, ..fields_rest],
112112-// [#(data_name, value), ..data_rest]
113113-// if field_name == data_name
114114-// ->
115115-// do_add_input_data(fields_rest, data_rest, [
116116-// input.set_value(field, value),
117117-// ..acc
118118-// ])
119119-// // at this point we still have fields and data left, but the first
120120-// // field and first data don't match. so we decide we've got no data
121121-// // for the first field and move on to the next. but we need to add
122122-// // this field without data to the accumulator
123123-// [field, ..fields_rest], _ ->
124124-// do_add_input_data(fields_rest, data, [field, ..acc])
125125-// }
126126-// }
127127-128128-// pub fn decodes(
129129-// form: Form(format, output, decoder, has_decoder),
130130-// decoder: decoder,
131131-// ) -> Form(format, output, decoder, HasDecoder) {
132132-// let Form(fields, parse_with, _) = form
133133-// Form(fields, parse_with, Some(decoder))
134134-// }
135135-136136-// pub fn parse(
137137-// form: Form(format, output, decoder, HasDecoder),
138138-// ) -> Result(output, Form(format, output, decoder, HasDecoder)) {
139139-// // we've tagged that we have a decoder with out has_decoder phantom type
140140-// // so we can get away with let assert here
141141-// let assert Form(fields, parse_with, Some(decoder)) = form
142142-// case parse_with(fields, decoder) {
143143-// Ok(output) -> Ok(output)
144144-// Error(fields) -> Error(Form(fields, parse_with, Some(decoder)))
145145-// }
146146-// }
147147-148148-// pub fn try(
149149-// form: Form(format, output, decoder, HasDecoder),
150150-// apply fun: fn(output, Form(format, output, decoder, HasDecoder)) ->
151151-// Result(c, Form(format, output, decoder, HasDecoder)),
152152-// ) -> Result(c, Form(format, output, decoder, HasDecoder)) {
153153-// parse(form) |> result.try(fun(_, form))
154154-// }
155155-156156-// pub fn get_inputs(form: Form(format, a, b, has_decoder)) -> List(Input(format)) {
157157-// form.fields |> list.reverse
158158-// }
159159-160160-// pub fn update_input(
161161-// form: Form(format, output, decoder, has_decoder),
162162-// name: String,
163163-// fun: fn(Input(format)) -> Input(format),
164164-// ) -> Form(format, output, decoder, has_decoder) {
165165-// form.fields
166166-// |> list.map(fn(field) {
167167-// case field.name == name {
168168-// True -> fun(field)
169169-// False -> field
170170-// }
171171-// })
172172-// |> Form(form.parse_with, form.decoder)
173173-// }
+94-104
formz/src/formz/formz_use.gleam
···11-import formz/field.{type Definition, type Field}
11+import formz/definition.{type Definition}
22+import formz/field
33+import formz/subform
44+import formz/widget
25import gleam/dict
36import gleam/list
47import gleam/result
···1215}
13161417pub type FormItem(format) {
1515- Item(input: Field(format))
1616- Set(Fieldset(format), items: List(FormItem(format)))
1717-}
1818-1919-pub type Fieldset(format) {
2020- Fieldset(prefix: String, label: String)
1818+ Element(field.Field, widget: widget.Widget(format))
1919+ Set(subform.SubForm, items: List(FormItem(format)))
2120}
22212322pub fn create_form(thing: thing) -> Form(format, thing) {
···2524}
26252726pub fn with(
2828- field: Field(format),
2929- definition: Definition(format, input_output),
3030- fun: fn(input_output) -> Form(format, form_output),
2727+ field: field.Field,
2828+ is definition: Definition(format, input_output),
2929+ rest fun: fn(input_output) -> Form(format, form_output),
3130) -> Form(format, form_output) {
3231 // we pass in our placeholder value, and we're going to throw away the
3332 // decoded result here, we just care about pulling out the fields
3433 // from the form.
3535- let next = fun(definition.placeholder)
3636-3737- let field = field |> field.set_widget(definition.widget)
3434+ let next_form = fun(definition.placeholder)
38353939- // prepend the new input to the inputs from the form we got in the
3636+ // prepend the new field to the items from the form we got in the
4037 // previous step.
4141- let updated_inputs = [Item(field), ..next.items]
3838+ let updated_items = [Element(field, definition.widget), ..next_form.items]
42394340 // now create the parse function. parse function accepts most recent
4441 // version of input list, since data can be added to it. the list
4542 // above we just needed for the initial setup.
4646- let parse = fn(inputs: List(FormItem(format))) {
4343+ let parse = fn(items: List(FormItem(format))) {
4744 // pull out the latest version of this field to get latest input data
4848- let assert Ok(#(Item(input), next_inputs)) = next_item(inputs)
4545+ let assert Ok(#(Element(field, widget), pop_elements)) = pop_element(items)
49465047 // transform the input data using the transform/validate/decode/etc function
5151- let input_output = definition.transform(input.value)
4848+ let input_output = definition.transform(field.value)
52495350 // pass our transformed input data to the next function/form. if
5451 // we errored we still do this with our placeholder so we can continue
5555- // processing all the fields in the form. we will return a form
5656- // with an error, so if we're on the error track we'll throw away
5757- // the "output" made with this and just keep the errors.
5252+ // processing all the fields in the form. if we're on the error track
5353+ // we'll throw away the "output" made with this and just keep the error
5854 let next_form = fun(input_output |> result.unwrap(definition.placeholder))
5959- let form_output = next_form.parse(next_inputs)
5555+ let form_output = next_form.parse(pop_elements)
60566157 // ok, check which track we're on
6258 case form_output, input_output {
···65616662 // form has errors, but this field was good, so add it to the list
6763 // of fields as is.
6868- Error(inputs), Ok(_value) -> Error([Item(input), ..inputs])
6464+ Error(items), Ok(_value) -> Error([Element(field, widget), ..items])
69657066 // form was good so far, but this field errored, so need to
7167 // mark this field as invalid and return all the fields we've got
7268 // so far
7369 Ok(_), Error(error) ->
7474- input
7070+ field
7571 |> field.set_error(error)
7676- |> Item
7777- |> list.prepend(next_inputs, _)
7272+ |> Element(widget)
7373+ |> list.prepend(pop_elements, _)
7874 |> Error
79758076 // form already has errors and this field errored, so add this field
8177 // to the list of errors
8282- Error(fields), Error(error) ->
8383- input
7878+ Error(items), Error(error) ->
7979+ field
8480 |> field.set_error(error)
8585- |> Item
8686- |> list.prepend(fields, _)
8181+ |> Element(widget)
8282+ |> list.prepend(items, _)
8783 |> Error
8884 }
8985 }
9090- Form(updated_inputs, parse: parse, placeholder: next.placeholder)
8686+ Form(items: updated_items, parse:, placeholder: next_form.placeholder)
9187}
92889393-pub fn sub_form(
9494- prefix: String,
9595- name: String,
8989+pub fn with_form(
9090+ subform: subform.SubForm,
9691 sub: Form(format, sub_output),
9792 fun: fn(sub_output) -> Form(format, form_output),
9893) -> Form(format, form_output) {
9999- let next = fun(sub.placeholder)
9494+ let next_form = fun(sub.placeholder)
10095101101- let sub_inputs =
9696+ let sub_items =
10297 sub.items
103103- |> map_inputs(fn(input) {
104104- input |> field.set_name(prefix <> "." <> input.name)
9898+ |> map_fields(fn(field) {
9999+ field |> field.set_name(subform.name <> "." <> field.name)
105100 })
106101107107- let updated_inputs = [Set(Fieldset(prefix, name), sub_inputs), ..next.items]
102102+ let updated_items = [Set(subform, sub_items), ..next_form.items]
108103109109- let parse = fn(inputs: List(FormItem(format))) {
104104+ let parse = fn(items: List(FormItem(format))) {
110105 // pull out the latest version of this field to get latest input data
111111- let assert [Set(Fieldset(_, name), items), ..next_inputs] = inputs
106106+ let assert [Set(subform, sub_items), ..next_items] = items
112107113113- let sub_output = sub.parse(items)
108108+ let sub_output = sub.parse(sub_items)
114109115110 let next_form = fun(sub_output |> result.unwrap(sub.placeholder))
116116- let form_output = next_form.parse(next_inputs)
111111+ let form_output = next_form.parse(next_items)
117112118113 // ok, check which track we're on
119114 case form_output, sub_output {
···122117123118 // form has errors, but this sub form was good, so add it to the list
124119 // of items as is.
125125- Error(inputs), Ok(_value) ->
126126- Error([Set(Fieldset(prefix, name), items), ..inputs])
120120+ Error(items), Ok(_value) -> Error([Set(subform, items), ..items])
127121128122 // form was good so far, but this sub form errored, so need to
129123 // hop on error track
130124 Ok(_), Error(error_fields) ->
131131- Error([Set(Fieldset(prefix, name), error_fields), ..next_inputs])
125125+ Error([Set(subform, error_fields), ..next_items])
132126133127 // form already has errors and this form errored, so add this field
134128 // to the list of errors
135129 Error(fields), Error(error_fields) ->
136136- Error(list.prepend(fields, Set(Fieldset(prefix, name), error_fields)))
130130+ Error(list.prepend(fields, Set(subform, error_fields)))
137131 }
138132 }
139139- Form(updated_inputs, parse: parse, placeholder: next.placeholder)
133133+ Form(updated_items, parse: parse, placeholder: next_form.placeholder)
140134}
141135142142-fn next_item(
136136+fn pop_element(
143137 items: List(FormItem(format)),
144138) -> Result(#(FormItem(format), List(FormItem(format))), Nil) {
145139 case items {
146140 [] -> Error(Nil)
147141 [only] -> Ok(#(only, []))
148148- [Item(input), ..rest] -> Ok(#(Item(input), rest))
149149- [Set(_, []), ..rest] -> next_item(rest)
142142+ [Element(..) as item, ..rest] -> Ok(#(item, rest))
143143+ [Set(_, []), ..rest] -> pop_element(rest)
150144 [Set(s, [first, ..rest_1]), ..rest_2] ->
151151- next_item(list.flatten([[first], [Set(s, rest_1)], rest_2]))
145145+ pop_element(list.flatten([[first], [Set(s, rest_1)], rest_2]))
152146 }
153147}
154148155155-fn map_inputs(
149149+fn map_fields(
156150 items: List(FormItem(format)),
157157- fun: fn(Field(format)) -> Field(format),
151151+ fun: fn(field.Field) -> field.Field,
158152) -> List(FormItem(format)) {
159153 list.map(items, fn(item) {
160154 case item {
161161- Item(input) -> Item(fun(input))
162162- Set(s, items) -> Set(s, map_inputs(items, fun))
155155+ Element(field, widget) -> Element(fun(field), widget)
156156+ Set(s, items) -> Set(s, map_fields(items, fun))
163157 }
164158 })
165159}
166160167167-fn get_inputs(form: Form(format, ouput)) {
168168- form.items |> do_get_inputs([]) |> list.reverse
169169-}
170170-171171-fn do_get_inputs(items: List(FormItem(format)), acc) {
172172- case items {
173173- [] -> acc
174174- [Item(input), ..rest] -> do_get_inputs(rest, [input, ..acc])
175175- [Set(_, items), ..rest] -> do_get_inputs(list.flatten([items, rest]), acc)
176176- }
177177-}
178178-179161pub fn data(
180162 form: Form(format, output),
181163 input_data: List(#(String, String)),
···183165 let data = dict.from_list(input_data)
184166 let Form(items, parse, placeholder) = form
185167 items
186186- |> map_inputs(fn(field) {
168168+ |> map_fields(fn(field) {
187169 case dict.get(data, field.name) {
188188- Ok(value) -> field.set_value(field, value)
170170+ Ok(value) -> field.set_string_value(field, value)
189171 Error(_) -> field
190172 }
191173 })
···193175}
194176195177pub fn parse(form: Form(format, output)) -> Result(output, Form(format, output)) {
196196- // we've tagged that we have a decoder with out has_decoder phantom type
197197- // so we can get away with let assert here
198198- let Form(fields, parse, placeholder) = form
199199- case parse(fields) {
178178+ case form.parse(form.items) {
200179 Ok(output) -> Ok(output)
201201- Error(fields) -> Error(Form(fields, parse, placeholder))
180180+ Error(items) -> Error(Form(..form, items:))
202181 }
203182}
204183205205-pub fn try(
184184+pub fn parse_try(
206185 form: Form(format, output),
207186 apply fun: fn(output, Form(format, output)) -> Result(c, Form(format, output)),
208187) -> Result(c, Form(format, output)) {
209188 parse(form) |> result.try(fun(_, form))
210189}
211190212212-pub fn get_items(form: Form(format, output)) -> List(FormItem(format)) {
191191+pub fn items(form: Form(format, output)) -> List(FormItem(format)) {
213192 form.items
214193}
215194216216-pub fn get_item(
195195+pub fn get(
217196 form: Form(format, output),
218218- prefix: String,
197197+ name: String,
219198) -> Result(FormItem(format), Nil) {
220199 form.items
221200 |> list.filter(fn(item) {
222201 case item {
223223- Item(i) if i.name == prefix -> True
224224- Set(s, _) if s.prefix == prefix -> True
202202+ Element(i, _) if i.name == name -> True
203203+ Set(s, _) if s.name == name -> True
225204 _ -> False
226205 }
227206 })
228207 |> list.first
229208}
230209231231-pub fn map_item(
210210+pub fn update(
232211 form: Form(format, output),
233212 name: String,
234234- fun: fn(FormItem(format)) -> a,
235235-) -> Result(a, Nil) {
236236- form |> get_item(name) |> result.map(fun)
213213+ fun: fn(FormItem(format)) -> FormItem(format),
214214+) {
215215+ form.items
216216+ |> do_formitem_update(name, fun)
217217+ |> Form(form.parse, form.placeholder)
237218}
238219239239-pub fn get_input(
240240- form: Form(format, output),
220220+fn do_formitem_update(
221221+ items: List(FormItem(format)),
241222 name: String,
242242-) -> Result(Field(format), Nil) {
243243- form
244244- |> get_inputs
245245- |> list.filter(fn(input) { input.name == name })
246246- |> list.first
223223+ fun: fn(FormItem(format)) -> FormItem(format),
224224+) -> List(FormItem(format)) {
225225+ items
226226+ |> list.map(fn(item) {
227227+ case item {
228228+ Element(i, _) if i.name == name -> fun(item)
229229+ Set(s, _) if s.name == name -> fun(item)
230230+ Set(s, items) -> Set(s, do_formitem_update(items, name, fun))
231231+ _ -> item
232232+ }
233233+ })
247234}
248235249249-pub fn map_input(
236236+pub fn update_field(
250237 form: Form(format, output),
251238 name: String,
252252- fun: fn(Field(format)) -> a,
253253-) -> Result(a, Nil) {
254254- form |> get_input(name) |> result.map(fun)
239239+ fun: fn(field.Field) -> field.Field,
240240+) -> Form(format, output) {
241241+ update(form, name, fn(item) {
242242+ case item {
243243+ Element(field, widget) -> Element(fun(field), widget)
244244+ _ -> item
245245+ }
246246+ })
255247}
256248257257-pub fn update_input(
249249+pub fn update_fieldset(
258250 form: Form(format, output),
259251 name: String,
260260- fun: fn(Field(format)) -> Field(format),
252252+ fun: fn(subform.SubForm) -> subform.SubForm,
261253) -> Form(format, output) {
262262- form.items
263263- |> map_inputs(fn(field) {
264264- case field.name == name {
265265- True -> fun(field)
266266- False -> field
254254+ update(form, name, fn(item) {
255255+ case item {
256256+ Set(subform, items) -> Set(fun(subform), items)
257257+ _ -> item
267258 }
268259 })
269269- |> Form(form.parse, form.placeholder)
270260}
···11import formz/field.{field}
22import formz/formz_use as formz
33-import formz_string/fields
33+import formz_string/definitions
4455pub fn make_form() {
66 let choices = [#("Yes", True), #("Maybe", True), #("No", False)]
7788- use a <- formz.with(field("a"), fields.text_field())
99- use b <- formz.with(field("b"), fields.integer_field())
1010- use c <- formz.with(field("c"), fields.number_field())
1111- use d <- formz.with(field("d"), fields.boolean_field())
1212- use e <- formz.with(field("e"), fields.email_field())
1313- use f <- formz.with(field("g"), fields.enum_field(letters()))
1414- use g <- formz.with(field("h"), fields.indexed_enum_field(choices))
1515- use h <- formz.with(field("i"), fields.list_field(["Dog", "Cat", "Bird"]))
88+ use a <- formz.with(field("a"), definitions.text_field())
99+ use b <- formz.with(field("b"), definitions.integer_field())
1010+ use c <- formz.with(field("c"), definitions.number_field())
1111+ use d <- formz.with(field("d"), definitions.boolean_field())
1212+ use e <- formz.with(field("e"), definitions.email_field())
1313+ use f <- formz.with(field("g"), definitions.enum_field(letters()))
1414+ use g <- formz.with(field("h"), definitions.indexed_enum_field(choices))
1515+ use h <- formz.with(
1616+ field("i"),
1717+ definitions.list_field(["Dog", "Cat", "Bird"]),
1818+ )
16191720 formz.create_form(#(a, b, c, d, e, f, g, h))
1821}