tablo_core/schema/
read.rs1use std::collections::{BTreeSet, HashMap, HashSet};
9
10use topcoat::Result;
11
12use super::{Schema, fields::ChoiceControl};
13use crate::form::{FormValue, FormValues};
14
15pub(super) struct Pairs<'p> {
17 pairs: &'p [(String, String)],
18 at: HashMap<&'p str, Vec<usize>>,
19 claimed: Vec<bool>,
20}
21
22impl<'p> Pairs<'p> {
23 fn new(pairs: &'p [(String, String)]) -> Self {
24 let mut at: HashMap<&str, Vec<usize>> = HashMap::new();
25 for (index, (key, _)) in pairs.iter().enumerate() {
26 at.entry(key.as_str()).or_default().push(index);
27 }
28 Self {
29 pairs,
30 at,
31 claimed: vec![false; pairs.len()],
32 }
33 }
34
35 pub(super) fn claim(&mut self, key: &str) -> Vec<&'p str> {
37 let Some(indices) = self.at.get(key) else {
38 return Vec::new();
39 };
40 indices
41 .iter()
42 .map(|&index| {
43 self.claimed[index] = true;
44 self.pairs[index].1.as_str()
45 })
46 .collect()
47 }
48
49 fn unclaimed(&self) -> Vec<String> {
51 self.pairs
52 .iter()
53 .zip(&self.claimed)
54 .filter(|(_, claimed)| !**claimed)
55 .map(|((key, _), _)| key.clone())
56 .collect::<BTreeSet<_>>()
57 .into_iter()
58 .collect()
59 }
60}
61
62impl<F> Schema<F> {
63 pub fn read(&self, posted: &[(String, String)]) -> std::result::Result<FormValues, String> {
78 self.read_claiming(&mut Pairs::new(posted))
79 }
80
81 pub(crate) fn read_unclaimed(
83 &self,
84 posted: &[(String, String)],
85 ) -> std::result::Result<(FormValues, Vec<String>), String> {
86 let mut pairs = Pairs::new(posted);
87 let values = self.read_claiming(&mut pairs)?;
88 Ok((values, pairs.unclaimed()))
89 }
90
91 fn read_claiming(&self, pairs: &mut Pairs<'_>) -> std::result::Result<FormValues, String> {
92 let mut values = FormValues::new();
93 for field in &self.fields {
94 let name = field.name();
95 if let Some(repeater) = field.as_repeater() {
96 if let Some(rows) = repeater.read(name, pairs)? {
97 values.insert(name, rows);
98 }
99 continue;
100 }
101 let posted = pairs.claim(name);
102 if field.as_choice().is_some_and(ChoiceControl::is_multiple) {
103 if !posted.is_empty() {
104 values.insert(name, distinct(&posted));
105 }
106 } else if let Some(last) = posted.last() {
107 values.insert(name, *last);
108 }
109 }
110 Ok(values)
111 }
112
113 pub(crate) fn read_input(
121 &self,
122 posted: &[(String, String)],
123 reserved: &[&str],
124 ) -> Result<FormValues> {
125 use topcoat::router::error::bad_request;
126
127 let (mut input, unclaimed) = self.read_unclaimed(posted).map_err(bad_request)?;
128 let unknown: Vec<String> = unclaimed
129 .into_iter()
130 .filter(|key| !reserved.contains(&key.as_str()))
131 .collect();
132 if !unknown.is_empty() {
133 return Err(bad_request(format!("unknown field(s): {}", unknown.join(", "))).into());
134 }
135 self.drop_disabled(&mut input, false);
137 for key in self.condition_hidden(&input) {
138 input.remove(&key);
139 }
140 Ok(input)
141 }
142}
143
144fn distinct(posted: &[&str]) -> FormValue {
147 let mut seen = HashSet::new();
148 FormValue::List(
149 posted
150 .iter()
151 .map(|value| value.trim())
152 .filter(|value| !value.is_empty() && seen.insert(*value))
153 .map(str::to_string)
154 .collect(),
155 )
156}
157
158#[cfg(test)]
159mod tests;