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