Skip to main content

tablo_core/form/
values.rs

1//! What a form holds: one typed value per key.
2
3use std::collections::BTreeMap;
4
5/// What a form holds, one [`FormValue`] per key: a submission its schema
6/// [read](crate::Schema::read), or a stored record as its record form spells it.
7///
8/// A record form's [`hydrate`](crate::RecordForm::hydrate) writes one and its
9/// [`parse`](crate::RecordForm::parse) reads one, as an [`ActionInput`](crate::ActionInput)'s parse
10/// does. Each key holds the shape its control posts: [`Text`](FormValue::Text) for a scalar
11/// control, a [`List`](FormValue::List) for a multiple choice, and [`Rows`](FormValue::Rows) for a
12/// repeater.
13///
14/// ```
15/// use tablo_core::FormValues;
16///
17/// let mut values = FormValues::new();
18/// values.insert("title", "Hello");
19/// values.insert("tags", vec!["rust".to_string(), "web".to_string()]);
20/// assert_eq!(values.text("title"), Some("Hello"));
21/// assert_eq!(values.list("tags").map(<[String]>::len), Some(2));
22/// assert_eq!(values.text("tags"), None);
23/// ```
24#[derive(Debug, Clone, Default, PartialEq, Eq)]
25pub struct FormValues {
26    values: BTreeMap<String, FormValue>,
27}
28
29impl FormValues {
30    /// No value.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Whether no key holds a value.
36    pub fn is_empty(&self) -> bool {
37        self.values.is_empty()
38    }
39
40    /// How many keys hold a value.
41    pub fn len(&self) -> usize {
42        self.values.len()
43    }
44
45    /// The value `key` holds.
46    pub fn get(&self, key: &str) -> Option<&FormValue> {
47        self.values.get(key)
48    }
49
50    /// The text `key` holds; `None` when it holds none, or a list or rows.
51    pub fn text(&self, key: &str) -> Option<&str> {
52        self.get(key).and_then(FormValue::as_text)
53    }
54
55    /// The list `key` holds; `None` when it holds none, or text or rows.
56    pub fn list(&self, key: &str) -> Option<&[String]> {
57        self.get(key).and_then(FormValue::as_list)
58    }
59
60    /// The rows `key` holds; `None` when it holds none, or text or a list.
61    pub fn rows(&self, key: &str) -> Option<&[FormValues]> {
62        self.get(key).and_then(FormValue::as_rows)
63    }
64
65    /// Whether `key` holds a value.
66    pub fn contains(&self, key: &str) -> bool {
67        self.values.contains_key(key)
68    }
69
70    /// Sets `key` to `value`, returning what it held.
71    pub fn insert(
72        &mut self,
73        key: impl Into<String>,
74        value: impl Into<FormValue>,
75    ) -> Option<FormValue> {
76        self.values.insert(key.into(), value.into())
77    }
78
79    /// Removes `key`, returning what it held.
80    pub fn remove(&mut self, key: &str) -> Option<FormValue> {
81        self.values.remove(key)
82    }
83
84    /// Every key holding a value, sorted.
85    pub fn keys(&self) -> impl Iterator<Item = &str> {
86        self.values.keys().map(String::as_str)
87    }
88
89    /// Every key and its value, sorted by key.
90    pub fn iter(&self) -> impl Iterator<Item = (&str, &FormValue)> {
91        self.values.iter().map(|(key, value)| (key.as_str(), value))
92    }
93}
94
95impl<K: Into<String>, V: Into<FormValue>> FromIterator<(K, V)> for FormValues {
96    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
97        let mut values = Self::new();
98        values.extend(iter);
99        values
100    }
101}
102
103/// Sets each key in turn: a later value replaces an earlier one.
104impl<K: Into<String>, V: Into<FormValue>> Extend<(K, V)> for FormValues {
105    fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
106        for (key, value) in iter {
107            self.insert(key, value);
108        }
109    }
110}
111
112impl IntoIterator for FormValues {
113    type Item = (String, FormValue);
114    type IntoIter = std::collections::btree_map::IntoIter<String, FormValue>;
115
116    fn into_iter(self) -> Self::IntoIter {
117        self.values.into_iter()
118    }
119}
120
121/// One key's value in [`FormValues`], in the shape its control posts.
122#[derive(Debug, Clone, PartialEq, Eq)]
123#[non_exhaustive]
124pub enum FormValue {
125    /// A scalar control's value, as typed: a text field's, a single choice's option, a toggle's
126    /// `true` or `false`, a file field's stored path.
127    Text(String),
128    /// A multiple choice's options. [`Schema::read`](crate::Schema::read) keeps each once and
129    /// none blank, in the order they posted.
130    List(Vec<String>),
131    /// A repeater's rows, in the order they show, each holding its item's values.
132    Rows(Vec<FormValues>),
133}
134
135impl FormValue {
136    /// The text this value holds, if it is one.
137    pub fn as_text(&self) -> Option<&str> {
138        match self {
139            Self::Text(text) => Some(text),
140            _ => None,
141        }
142    }
143
144    /// The list this value holds, if it is one.
145    pub fn as_list(&self) -> Option<&[String]> {
146        match self {
147            Self::List(items) => Some(items),
148            _ => None,
149        }
150    }
151
152    /// The rows this value holds, if it is rows.
153    pub fn as_rows(&self) -> Option<&[FormValues]> {
154        match self {
155            Self::Rows(rows) => Some(rows),
156            _ => None,
157        }
158    }
159}
160
161impl From<String> for FormValue {
162    fn from(text: String) -> Self {
163        Self::Text(text)
164    }
165}
166
167impl From<&str> for FormValue {
168    fn from(text: &str) -> Self {
169        Self::Text(text.to_string())
170    }
171}
172
173impl From<&String> for FormValue {
174    fn from(text: &String) -> Self {
175        Self::Text(text.clone())
176    }
177}
178
179impl From<Vec<String>> for FormValue {
180    fn from(items: Vec<String>) -> Self {
181        Self::List(items)
182    }
183}
184
185impl From<Vec<FormValues>> for FormValue {
186    fn from(rows: Vec<FormValues>) -> Self {
187        Self::Rows(rows)
188    }
189}