Skip to main content

tablo_core/
values.rs

1//! Form values: what a form holds, one typed value per key; the [`FormScalar`] types a key reads
2//! and writes, the [`TypedValue`]s among them, and the [`FieldError`]s a key is refused with.
3//!
4//! The module sits below `schema`, which reads and renders form values, and `form`, whose record
5//! forms hydrate and parse them; `form` re-exports it.
6
7mod error;
8mod parse;
9mod scalar;
10mod typed;
11mod zone;
12
13use std::collections::BTreeMap;
14
15pub(crate) use error::unreadable;
16pub use error::{FieldError, FieldErrorKind, FieldErrors};
17pub use parse::{parse_list, parse_password, parse_scalar, write_list};
18pub use scalar::{FormScalar, NullableScalar, assert_form_scalar};
19pub use typed::TypedValue;
20pub(crate) use typed::{is_email, is_web_url, read_datetime_local};
21pub(crate) use zone::ZoneSource;
22pub use zone::time_zone;
23
24/// What a form holds, one [`FormValue`] per key: a submission its schema
25/// [read](crate::Schema::read), or a stored record as its record form spells it.
26///
27/// A record form's [`hydrate`](crate::RecordForm::hydrate) writes one and its
28/// [`parse`](crate::RecordForm::parse) reads one, as a [`FormInput`](crate::FormInput)'s parse
29/// does. Each key holds the shape its control posts: [`Text`](FormValue::Text) for a scalar
30/// control, a [`List`](FormValue::List) for a multiple choice, and [`Rows`](FormValue::Rows) for a
31/// repeater.
32///
33/// ```
34/// use tablo_core::FormValues;
35///
36/// let mut values = FormValues::new();
37/// values.insert("title", "Hello");
38/// values.insert("tags", vec!["rust".to_string(), "web".to_string()]);
39/// assert_eq!(values.text("title"), Some("Hello"));
40/// assert_eq!(values.list("tags").map(<[String]>::len), Some(2));
41/// assert_eq!(values.text("tags"), None);
42/// ```
43#[derive(Debug, Clone, Default, PartialEq, Eq)]
44pub struct FormValues {
45    values: BTreeMap<String, FormValue>,
46}
47
48impl FormValues {
49    /// No value.
50    pub fn new() -> Self {
51        Self::default()
52    }
53
54    /// Whether no key holds a value.
55    pub fn is_empty(&self) -> bool {
56        self.values.is_empty()
57    }
58
59    /// How many keys hold a value.
60    pub fn len(&self) -> usize {
61        self.values.len()
62    }
63
64    /// The value `key` holds.
65    pub fn get(&self, key: &str) -> Option<&FormValue> {
66        self.values.get(key)
67    }
68
69    /// The text `key` holds; `None` when it holds none, or a list or rows.
70    pub fn text(&self, key: &str) -> Option<&str> {
71        self.get(key).and_then(FormValue::as_text)
72    }
73
74    /// The list `key` holds; `None` when it holds none, or text or rows.
75    pub fn list(&self, key: &str) -> Option<&[String]> {
76        self.get(key).and_then(FormValue::as_list)
77    }
78
79    /// The rows `key` holds; `None` when it holds none, or text or a list.
80    pub fn rows(&self, key: &str) -> Option<&[FormValues]> {
81        self.get(key).and_then(FormValue::as_rows)
82    }
83
84    /// Whether `key` holds a value.
85    pub fn contains(&self, key: &str) -> bool {
86        self.values.contains_key(key)
87    }
88
89    /// Sets `key` to `value`, returning what it held.
90    pub fn insert(
91        &mut self,
92        key: impl Into<String>,
93        value: impl Into<FormValue>,
94    ) -> Option<FormValue> {
95        self.values.insert(key.into(), value.into())
96    }
97
98    /// Removes `key`, returning what it held.
99    pub fn remove(&mut self, key: &str) -> Option<FormValue> {
100        self.values.remove(key)
101    }
102
103    /// Every key holding a value, sorted.
104    pub fn keys(&self) -> impl Iterator<Item = &str> {
105        self.values.keys().map(String::as_str)
106    }
107
108    /// Every key and its value, sorted by key.
109    pub fn iter(&self) -> impl Iterator<Item = (&str, &FormValue)> {
110        self.values.iter().map(|(key, value)| (key.as_str(), value))
111    }
112}
113
114impl<K: Into<String>, V: Into<FormValue>> FromIterator<(K, V)> for FormValues {
115    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
116        let mut values = Self::new();
117        values.extend(iter);
118        values
119    }
120}
121
122/// Sets each key in turn: a later value replaces an earlier one.
123impl<K: Into<String>, V: Into<FormValue>> Extend<(K, V)> for FormValues {
124    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
125        for (key, value) in iter {
126            self.insert(key, value);
127        }
128    }
129}
130
131impl IntoIterator for FormValues {
132    type Item = (String, FormValue);
133    type IntoIter = std::collections::btree_map::IntoIter<String, FormValue>;
134
135    fn into_iter(self) -> Self::IntoIter {
136        self.values.into_iter()
137    }
138}
139
140/// One key's value in [`FormValues`], in the shape its control posts.
141#[derive(Debug, Clone, PartialEq, Eq)]
142#[non_exhaustive]
143pub enum FormValue {
144    /// A scalar control's value, as typed: a text field's, a single choice's option, a toggle's
145    /// `true` or `false`, a file field's stored path.
146    Text(String),
147    /// A multiple choice's options. [`Schema::read`](crate::Schema::read) keeps each once and
148    /// none blank, in the order they posted.
149    List(Vec<String>),
150    /// A repeater's rows, in the order they show, each holding its item's values.
151    Rows(Vec<FormValues>),
152}
153
154impl FormValue {
155    /// The text this value holds, if it is one.
156    pub fn as_text(&self) -> Option<&str> {
157        match self {
158            Self::Text(text) => Some(text),
159            _ => None,
160        }
161    }
162
163    /// The list this value holds, if it is one.
164    pub fn as_list(&self) -> Option<&[String]> {
165        match self {
166            Self::List(items) => Some(items),
167            _ => None,
168        }
169    }
170
171    /// The rows this value holds, if it is rows.
172    pub fn as_rows(&self) -> Option<&[FormValues]> {
173        match self {
174            Self::Rows(rows) => Some(rows),
175            _ => None,
176        }
177    }
178}
179
180impl From<String> for FormValue {
181    fn from(text: String) -> Self {
182        Self::Text(text)
183    }
184}
185
186impl From<&str> for FormValue {
187    fn from(text: &str) -> Self {
188        Self::Text(text.to_string())
189    }
190}
191
192impl From<&String> for FormValue {
193    fn from(text: &String) -> Self {
194        Self::Text(text.clone())
195    }
196}
197
198impl From<Vec<String>> for FormValue {
199    fn from(items: Vec<String>) -> Self {
200        Self::List(items)
201    }
202}
203
204impl From<Vec<FormValues>> for FormValue {
205    fn from(rows: Vec<FormValues>) -> Self {
206        Self::Rows(rows)
207    }
208}
209
210/// One record-form field and the form keys it binds.
211#[derive(Debug, Clone)]
212pub struct FormField<K> {
213    /// The field, as the form's field enum names it.
214    pub field: K,
215    /// The Rust field name, for build-time refusals.
216    pub name: &'static str,
217    /// The keys the field binds: one for a scalar, every key of an embedded
218    /// value (an enum's discriminant first). An error on the field renders
219    /// under the first.
220    pub keys: Vec<String>,
221    /// The keys with no blank answer, which an empty submission refuses: the
222    /// panel renders their controls required.
223    pub required: Vec<String>,
224}