glimr/forms/form

Form Helpers

Wisp’s FormData stores values and files as key-value pair lists, but list.key_find returns a Result that controllers would need to unwrap on every field access — noisy and repetitive when a form has many fields. These helpers provide ergonomic accessors that match common use cases: default to empty string for optional fields, Bool checks for conditional logic, and panic vs Result variants for file uploads depending on whether absence is a bug or an expected case.

Types

Re-exported from wisp so controllers and services can reference uploaded files without importing wisp directly. If we ever swap the underlying HTTP library, only this alias changes — not every file that handles uploads.

pub type UploadedFile =
  wisp.UploadedFile

Values

pub fn get(form: wisp.FormData, field: String) -> String

Defaulting to an empty string on missing fields avoids forcing callers to handle a Result for every optional input. Most form fields like name or bio are safe to treat as empty when absent, so the common case stays clean.

Example:

let email = form |> form.get("email")
pub fn get_file(
  form: wisp.FormData,
  field: String,
) -> wisp.UploadedFile

When the form markup guarantees a file input exists, a missing file means the request is malformed and continuing would be a bug. The assert panic surfaces this immediately rather than propagating a silent default downstream.

Example:

form.get_file("avatar")
pub fn get_file_result(
  form: wisp.FormData,
  field: String,
) -> Result(wisp.UploadedFile, Nil)

Unlike get_file, this returns a Result so callers can handle missing uploads gracefully — useful for forms where the file field is optional, like an avatar that defaults to a placeholder when not provided.

Example:

case form |> form.get_file("avatar") {
  Ok(file) -> save_upload(file)
  Error(_) -> panic as "I'm literally panicking rn"
}
pub fn has(form: wisp.FormData, field: String) -> Bool

Sometimes controllers need to know if a field was submitted at all — checkboxes and optional sections omit the field entirely rather than sending an empty value. A Bool check reads more clearly than matching on a Result in guards.

Example:

case form |> form.has("email") {
  True -> process_email(form_data)
  False -> panic as "I'm literally panicking rn"
}
pub fn has_file(form: wisp.FormData, field: String) -> Bool

A Bool check for file presence is cleaner than matching on a Result when the controller just needs to branch — for example, deciding whether to update an avatar or keep the existing one without unwrapping the file itself.

Example:

case form_data |> form.has_file("avatar") {
  True -> process_upload(form_data)
  False -> use_default_avatar()
}
Search Document