tablo_core/schema/input.rs
1//! A typed value a form posts under keys no column binds: the [`FormInput`] trait.
2
3use std::{fmt::Debug, hash::Hash};
4
5use topcoat::context::Cx;
6
7use super::{Field, Schema};
8use crate::values::{FieldError, FormValues};
9
10/// A typed value a form posts under its own keys, which no column binds: what an
11/// [`Action`](crate::Action) asks for before it runs, what a sign-up reads, one item of a
12/// repeater.
13///
14/// Derive it with [`FormInput`](derive@crate::FormInput) on a struct of form scalars, or name
15/// `()` for a value that asks for nothing.
16///
17/// An action with input asks for it before it runs: its button opens the input form in a dialog
18/// over the page, and its submit runs the action with the parsed value. A refused value renders
19/// the form as a page with the field's error, and writes nothing.
20///
21/// A repeater stores its items in a `#[document]` list, so an item is also a `toasty::Embed`
22/// struct. Each row posts the item's keys under its own prefix, and [`write`](Self::write) fills
23/// a stored item's row on the edit page.
24pub trait FormInput: Sized + Send + Sync + 'static {
25 /// One variant per field: what [`Action::validate_input`](crate::Action::validate_input)
26 /// refuses a value under, so an error always names a field the form renders. The derive
27 /// names it after the struct: `Closing` declares `ClosingField`, whose `reason` field is
28 /// `ClosingField::Reason`. An input no hook refuses by field names
29 /// [`Infallible`](std::convert::Infallible).
30 type Field: Copy + Eq + Hash + Debug + Send + Sync + 'static;
31
32 /// The form key `field` posts under.
33 fn key(field: Self::Field) -> &'static str;
34
35 /// The value's controls, each posting its own key. An empty schema asks for nothing: an
36 /// action runs on its button.
37 fn schema() -> Schema;
38
39 /// Parse a submission of [`schema`](Self::schema)'s keys.
40 ///
41 /// # Errors
42 ///
43 /// Every key that failed, each once.
44 fn parse(cx: &Cx, values: &FormValues) -> Result<Self, Vec<FieldError>>;
45
46 /// Write the value's keys as its form posts them: a repeater fills a stored item's row from
47 /// them, and its detail-page column reads them. A password writes nothing, as it never renders
48 /// back into a form. Nothing writes an action's input or a sign-up, so a type used only there
49 /// writes nothing too.
50 fn write(&self, out: &mut FormValues);
51}
52
53/// No input: an action runs on its button.
54impl FormInput for () {
55 type Field = std::convert::Infallible;
56
57 fn key(field: Self::Field) -> &'static str {
58 match field {}
59 }
60
61 fn schema() -> Schema {
62 Schema::empty()
63 }
64
65 fn parse(_cx: &Cx, _values: &FormValues) -> Result<Self, Vec<FieldError>> {
66 Ok(())
67 }
68
69 fn write(&self, _out: &mut FormValues) {}
70}
71
72/// Renders `field`'s control required: the derive marks each field with no blank answer.
73#[doc(hidden)]
74pub fn required_input(field: impl Into<Field>) -> Field {
75 let mut field = field.into();
76 field.set_required(true);
77 field
78}