pub struct NestedForm<T>(pub T);Expand description
Form extractor for bracketed field names, like Rails’ params (post[title], tag_ids[], lines[0][qty]).
axum’s Form only knows flat names (title=...); use it for ordinary
forms. NestedForm is for forms that send a list or nested records:
| Field name | Becomes |
|---|---|
title | the field title |
post[title] | the field title of the struct in field post |
tag_ids[] (repeated) | a Vec with one item per value, in order |
lines[0][qty], lines[1][qty] | a Vec of structs, sorted by index (Rails’ fields_for / nested attributes) |
lines[][qty], lines[][price] | a Vec of structs; a name already set in the last item starts a new one |
A name sent twice keeps the last value (so a hidden done=0 before the
checkbox done=1 works as in Rails). Values are text: numbers and
booleans are parsed from it (1/true/on/yes are true, 0,
false, off, no and an empty value are false), an empty value is
None for an Option, and a single value fills a one-item Vec. Enums
with unit variants read their variant name.
On GET and HEAD it reads the query string; otherwise the body, which
must be application/x-www-form-urlencoded (2 MB at most, axum’s
default limit). A body that does not decode into T (a missing field, a
number that is not one, a=1&a[b]=2) is a 400, as an HTML page for
browsers (feature html) or JSON for API clients. Pure CPU, no binding
call.
§Examples
use ocre::NestedForm;
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
struct Order {
customer: Customer,
#[serde(default)]
tag_ids: Vec<i64>,
lines: Vec<Line>,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Customer {
name: String,
}
#[derive(Debug, Deserialize, PartialEq)]
struct Line {
product: String,
qty: u32,
#[serde(default)]
remove: bool,
}
let body = "customer[name]=Ada&tag_ids[]=3&tag_ids[]=7\
&lines[0][product]=Tea&lines[0][qty]=2\
&lines[1][product]=Cake&lines[1][qty]=1&lines[1][remove]=0&lines[1][remove]=1";
let order: Order = NestedForm::parse(body).unwrap();
assert_eq!(order.customer.name, "Ada");
assert_eq!(order.tag_ids, [3, 7]);
assert_eq!(order.lines[1], Line { product: "Cake".into(), qty: 1, remove: true });In a handler, like axum’s Form:
use axum::response::Redirect;
use ocre::{NestedForm, Result};
async fn create(NestedForm(order): NestedForm<Order>) -> Result<Redirect> {
Ok(Redirect::to("/orders"))
}Tuple Fields§
§0: TImplementations§
Source§impl<T: DeserializeOwned> NestedForm<T>
impl<T: DeserializeOwned> NestedForm<T>
Sourcepub fn parse(input: &str) -> Result<T>
pub fn parse(input: &str) -> Result<T>
Decodes an application/x-www-form-urlencoded string (a body or a query string) with bracketed names.
The rules are those of NestedForm; errors are
Error::BadRequest explaining which field failed.
§Examples
use ocre::NestedForm;
use std::collections::BTreeMap;
// `?filter[status]=open&filter[author]=ada` on an index page.
let filter: BTreeMap<String, BTreeMap<String, String>> =
NestedForm::parse("filter[status]=open&filter[author]=ada").unwrap();
assert_eq!(filter["filter"]["author"], "ada");
assert!(NestedForm::<BTreeMap<String, u32>>::parse("n=many").is_err());Trait Implementations§
Source§impl<T: Clone> Clone for NestedForm<T>
impl<T: Clone> Clone for NestedForm<T>
Source§fn clone(&self) -> NestedForm<T>
fn clone(&self) -> NestedForm<T>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more