1use crate::binding::bind_ordered;
2use crate::history::{
3 can_redo, can_undo, empty_history, record_change, redo_change, undo_change, History,
4 HistoryResult,
5};
6use crate::template::member_ordered_template;
7use crate::template::{data_only, repeats};
8use crate::util::{segments, value_at};
9use crate::view::{
10 initial_view, rekey_row_view, remove_row_view, set_all_expanded_view, toggle_row_view,
11 ViewState,
12};
13use crate::{BindOptions, FieldTemplate, FormError, FormResult, FormTemplate};
14use serde::{Deserialize, Serialize};
15use serde_json::{json, Map, Value};
16
17pub fn sequence_row_key(sequence: &str) -> FormResult<String> {
19 if sequence.is_empty() || sequence.len() > 13 || !sequence.bytes().all(|c| c.is_ascii_digit()) {
20 return Err(FormError::input(
21 "A sequence must contain 1–13 decimal digits",
22 ));
23 }
24 Ok(format!("__{sequence:0>13}__"))
25}
26
27pub fn create_row_key() -> FormResult<String> {
29 let mut bytes = [0u8; 7];
30 getrandom::fill(&mut bytes)
31 .map_err(|e| FormError::input(format!("Unable to generate a row key: {e}")))?;
32 let digits = bytes.iter().map(|b| format!("{b:02x}")).collect::<String>();
33 Ok(format!("__{}__", &digits[..13]))
34}
35
36fn check_key(key: &str) -> FormResult<()> {
37 if key.is_empty()
38 || !key
39 .bytes()
40 .all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
41 || key.bytes().all(|b| b.is_ascii_digit())
42 || ["__proto__", "prototype", "constructor"].contains(&key)
43 {
44 return Err(FormError::input(format!(
45 "Invalid row key: {key}; use sequenceRowKey for numeric ids"
46 )));
47 }
48 Ok(())
49}
50
51fn checked_segments(path: &str) -> FormResult<Vec<String>> {
52 let parts = segments(path);
53 if parts.is_empty()
54 || parts
55 .iter()
56 .any(|s| ["__proto__", "prototype", "constructor"].contains(&s.as_str()))
57 {
58 return Err(FormError::input(format!("Invalid form path: {path}")));
59 }
60 Ok(parts)
61}
62
63fn row_path(collection: &[String], key: &str) -> String {
65 collection
66 .iter()
67 .map(String::as_str)
68 .chain([key])
69 .collect::<Vec<_>>()
70 .join(".")
71}
72
73fn put_at(data: &Value, path: &[String], value: Value) -> Value {
74 let mut object = data.as_object().cloned().unwrap_or_default();
75 let head = &path[0];
76 let next = if path.len() == 1 {
77 value
78 } else {
79 put_at(object.get(head).unwrap_or(&Value::Null), &path[1..], value)
80 };
81 object.insert(head.clone(), next);
82 object.into()
83}
84
85fn fresh_key(used: &Map<String, Value>) -> FormResult<String> {
86 for _ in 0..100 {
87 let key = create_row_key()?;
88 if !used.contains_key(&key) {
89 return Ok(key);
90 }
91 }
92 Err(FormError::input("Unable to generate an unused row key"))
93}
94
95const INITIAL_ROW_KEY: &str = "__0000000000000__";
97
98fn normalize_row(
99 field: &FieldTemplate,
100 value: Option<&Value>,
101 path: &str,
102 fresh: bool,
103) -> FormResult<Value> {
104 if field.spec.get("type").is_some_and(|t| t == "group") {
105 normalize_fields(&field.children, value, path, fresh)
106 } else {
107 Ok(value
108 .or_else(|| field.spec.get("default"))
109 .cloned()
110 .unwrap_or(json!("")))
111 }
112}
113
114fn normalize_fields(
118 fields: &[FieldTemplate],
119 value: Option<&Value>,
120 path: &str,
121 fresh: bool,
122) -> FormResult<Value> {
123 let mut data = match value {
124 None => Map::new(),
125 Some(Value::Object(map)) => map.clone(),
126 _ if path.is_empty() => return Err(FormError::input("Form data must be an object")),
127 _ => {
128 return Err(FormError::input(format!(
129 "Group data must be an object: {path}"
130 )))
131 }
132 };
133 for field in fields {
134 let raw = data.get(&field.name);
135 let field_path = if path.is_empty() {
136 field.name.clone()
137 } else {
138 format!("{path}.{}", field.name)
139 };
140 if repeats(field) {
141 let mut rows = Map::new();
142 match raw {
143 None if data_only(field) => {}
145 None => {
146 let key = if fresh {
147 fresh_key(&rows)?
148 } else {
149 INITIAL_ROW_KEY.to_owned()
150 };
151 let row = normalize_row(field, None, &format!("{field_path}.{key}"), fresh)?;
152 rows.insert(key, row);
153 }
154 Some(Value::Object(map)) => {
155 for (key, value) in map {
156 check_key(key)?;
157 let row = normalize_row(
158 field,
159 Some(value),
160 &format!("{field_path}.{key}"),
161 fresh,
162 )?;
163 rows.insert(key.clone(), row);
164 }
165 }
166 _ => {
167 return Err(FormError::input(format!(
168 "Repeated data must be a keyed object: {field_path}"
169 )))
170 }
171 }
172 data.insert(field.name.clone(), rows.into());
173 } else if field.spec.get("type").is_some_and(|t| t == "group") {
174 let group = normalize_fields(&field.children, raw, &field_path, fresh)?;
175 data.insert(field.name.clone(), group);
176 } else if raw.is_none() {
177 if let Some(default) = field.spec.get("default") {
178 data.insert(field.name.clone(), default.clone());
179 }
180 }
181 }
182 Ok(data.into())
183}
184
185fn copy_row_value(field: &FieldTemplate, value: &Value) -> FormResult<Value> {
186 if field.spec.get("type").is_none_or(|t| t != "group") {
187 return Ok(value.clone());
188 }
189 let mut row = value.as_object().cloned().unwrap_or_default();
190 for child in &field.children {
191 let Some(raw) = row.get(&child.name).filter(|v| v.is_object()) else {
192 continue;
193 };
194 if repeats(child) {
195 let mut used = raw.as_object().unwrap().clone();
196 let mut copied = Map::new();
197 for value in raw.as_object().unwrap().values() {
198 let key = fresh_key(&used)?;
199 used.insert(key.clone(), Value::Null);
200 copied.insert(key, copy_row_value(child, value)?);
201 }
202 row.insert(child.name.clone(), copied.into());
203 } else if child.spec.get("type").is_some_and(|t| t == "group") {
204 let copied = copy_row_value(child, raw)?;
205 row.insert(child.name.clone(), copied);
206 }
207 }
208 Ok(row.into())
209}
210
211#[derive(Debug, Default, Clone, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct AddRowOptions {
215 pub key: Option<String>,
217 pub after_key: Option<String>,
219 #[serde(
221 default,
222 deserialize_with = "present_value",
223 skip_serializing_if = "Option::is_none"
224 )]
225 pub value: Option<Value>,
226}
227
228fn present_value<'de, D: serde::Deserializer<'de>>(
229 deserializer: D,
230) -> Result<Option<Value>, D::Error> {
231 Value::deserialize(deserializer).map(Some)
232}
233
234#[derive(Clone)]
236pub struct Form {
237 template: FormTemplate,
238 data: Value,
239 fields: Vec<Value>,
240 options: BindOptions,
241 revision: u64,
242 view: ViewState,
243 history: History<Value>,
244}
245
246impl Form {
247 pub fn new(template: FormTemplate, data: &Value, options: BindOptions) -> FormResult<Self> {
249 let template = member_ordered_template(&template);
251 let data = normalize_fields(&template.fields, Some(data), "", false)?;
252 let fields = bind_ordered(&template, &data, &options)?;
253 Ok(Self {
254 template,
255 data,
256 fields,
257 options,
258 revision: 0,
259 view: initial_view(),
260 history: empty_history(),
261 })
262 }
263
264 pub(crate) fn language(&self) -> &str {
266 self.options.language.as_str().unwrap_or("ko")
267 }
268
269 pub fn template(&self) -> &FormTemplate {
271 &self.template
272 }
273
274 pub fn fields(&self) -> &[Value] {
276 &self.fields
277 }
278
279 pub fn revision(&self) -> u64 {
281 self.revision
282 }
283
284 pub fn get_data(&self) -> Value {
286 self.data.clone()
287 }
288
289 pub fn set_data(&mut self, data: &Value) -> FormResult<()> {
291 self.commit_view(
292 normalize_fields(&self.template.fields, Some(data), "", false)?,
293 initial_view(),
294 empty_history(),
295 )
296 }
297
298 pub fn get_value(&self, path: &str) -> FormResult<Value> {
300 checked_segments(path)?;
301 Ok(value_at(&self.data, path).cloned().unwrap_or(Value::Null))
302 }
303
304 pub fn set_value(&mut self, path: &str, value: Value) -> FormResult<()> {
306 let segments = checked_segments(path)?;
307 let data = put_at(&self.data, &segments, value);
308 self.commit(
309 normalize_fields(&self.template.fields, Some(&data), "", false)?,
310 Some(&segments.join(".")),
311 )
312 }
313
314 pub fn undo(&mut self) -> FormResult<()> {
316 let result = undo_change(&self.history, self.data.clone())?;
317 self.restore(result)
318 }
319
320 pub fn redo(&mut self) -> FormResult<()> {
322 let result = redo_change(&self.history, self.data.clone())?;
323 self.restore(result)
324 }
325
326 pub fn can_undo(&self) -> bool {
328 can_undo(&self.history)
329 }
330
331 pub fn can_redo(&self) -> bool {
333 can_redo(&self.history)
334 }
335
336 fn restore(&mut self, result: HistoryResult<Value>) -> FormResult<()> {
338 let fields = bind_ordered(
339 &self.template,
340 &result.value,
341 &self.bind_options(&self.view),
342 )?;
343 self.history = result.history;
344 self.data = result.value;
345 self.fields = fields;
346 self.revision += 1;
347 Ok(())
348 }
349
350 fn bind_options(&self, view: &ViewState) -> BindOptions {
352 let mut options = self.options.clone();
353 options.collapsed = view
354 .collapsed
355 .iter()
356 .cloned()
357 .map(Value::String)
358 .collect::<Vec<_>>()
359 .into();
360 options
361 }
362
363 fn commit(&mut self, data: Value, path: Option<&str>) -> FormResult<()> {
366 let view = self.view.clone();
367 let history = self.recorded(path);
368 self.commit_view(data, view, history)
369 }
370
371 fn recorded(&self, path: Option<&str>) -> History<Value> {
373 record_change(&self.history, self.data.clone(), path)
374 }
375
376 fn commit_view(
378 &mut self,
379 data: Value,
380 view: ViewState,
381 history: History<Value>,
382 ) -> FormResult<()> {
383 let fields = bind_ordered(&self.template, &data, &self.bind_options(&view))?;
384 self.data = data;
385 self.fields = fields;
386 self.view = view;
387 self.history = history;
388 self.revision += 1;
389 Ok(())
390 }
391
392 pub fn toggle_row(&mut self, path: &str, key: &str) -> FormResult<()> {
394 let (_, rows) = self.collection(path)?;
395 if !rows.contains_key(key) {
396 return Err(FormError::input(format!("Unknown row: {key}")));
397 }
398 let segments = checked_segments(path)?;
399 self.refresh_view(toggle_row_view(&self.view, &row_path(&segments, key)))
400 }
401
402 pub fn set_all_expanded(&mut self, expanded: bool) -> FormResult<()> {
404 self.refresh_view(set_all_expanded_view(&self.fields, expanded))
405 }
406
407 fn refresh_view(&mut self, view: ViewState) -> FormResult<()> {
408 self.fields = bind_ordered(&self.template, &self.data, &self.bind_options(&view))?;
409 self.view = view;
410 Ok(())
411 }
412
413 fn editable_collection(&self, path: &str) -> FormResult<(&FieldTemplate, &Map<String, Value>)> {
415 let found = self.collection(path)?;
416 if data_only(found.0) {
417 return Err(FormError::input(format!(
418 "Rows of {path} come only from data"
419 )));
420 }
421 Ok(found)
422 }
423
424 fn collection(&self, path: &str) -> FormResult<(&FieldTemplate, &Map<String, Value>)> {
425 let parts = checked_segments(path)?;
426 let mut fields = &self.template.fields;
427 let mut selected = None;
428 let mut i = 0;
429 while i < parts.len() {
430 let field = fields
431 .iter()
432 .find(|f| f.name == parts[i])
433 .ok_or_else(|| FormError::input(format!("Unknown collection: {path}")))?;
434 if i + 1 == parts.len() {
435 selected = Some(field);
436 break;
437 }
438 if repeats(field) {
439 i += 1;
440 }
441 fields = &field.children;
442 i += 1;
443 }
444 let field = selected
445 .filter(|f| repeats(f))
446 .ok_or_else(|| FormError::input(format!("Not a keyed collection: {path}")))?;
447 let rows = value_at(&self.data, path)
448 .and_then(Value::as_object)
449 .ok_or_else(|| FormError::input(format!("Not a keyed collection: {path}")))?;
450 Ok((field, rows))
451 }
452
453 pub fn add_row(&mut self, path: &str, options: AddRowOptions) -> FormResult<String> {
455 let (field, rows) = self.editable_collection(path)?;
456 if field
457 .spec
458 .get("multiple")
459 .and_then(|v| v.get("max"))
460 .and_then(Value::as_f64)
461 .is_some_and(|max| rows.len() as f64 >= max)
462 {
463 return Err(FormError::input(format!(
464 "Maximum row count reached: {path}"
465 )));
466 }
467 let key = match options.key {
468 Some(key) => key,
469 None => fresh_key(rows)?,
470 };
471 check_key(&key)?;
472 if rows.contains_key(&key) {
473 return Err(FormError::input(format!("Row key already exists: {key}")));
474 }
475 let mut entries = rows
476 .iter()
477 .map(|(k, v)| (k.clone(), v.clone()))
478 .collect::<Vec<_>>();
479 let at = match &options.after_key {
480 None => entries.len(),
481 Some(after) => {
482 entries
483 .iter()
484 .position(|(k, _)| k == after)
485 .ok_or_else(|| FormError::input(format!("Unknown row: {after}")))?
486 + 1
487 }
488 };
489 let row_path = format!("{}.{key}", checked_segments(path)?.join("."));
490 entries.insert(
491 at,
492 (
493 key.clone(),
494 normalize_row(field, options.value.as_ref(), &row_path, true)?,
495 ),
496 );
497 self.commit(
498 put_at(
499 &self.data,
500 &checked_segments(path)?,
501 Value::Object(entries.into_iter().collect()),
502 ),
503 None,
504 )?;
505 Ok(key)
506 }
507
508 pub fn copy_row(
510 &mut self,
511 path: &str,
512 key: &str,
513 mut options: AddRowOptions,
514 ) -> FormResult<String> {
515 let (field, rows) = self.editable_collection(path)?;
516 let value = rows
517 .get(key)
518 .ok_or_else(|| FormError::input(format!("Unknown row: {key}")))?;
519 options.value = Some(copy_row_value(field, value)?);
520 if options.after_key.is_none() {
521 options.after_key = Some(key.into());
522 }
523 self.add_row(path, options)
524 }
525
526 pub fn remove_row(&mut self, path: &str, key: &str) -> FormResult<()> {
528 let (field, rows) = self.editable_collection(path)?;
529 if !rows.contains_key(key) {
530 return Err(FormError::input(format!("Unknown row: {key}")));
531 }
532 if field
533 .spec
534 .get("multiple")
535 .and_then(|v| v.get("min"))
536 .and_then(Value::as_f64)
537 .is_some_and(|min| rows.len() as f64 <= min)
538 {
539 return Err(FormError::input(format!(
540 "Minimum row count reached: {path}"
541 )));
542 }
543 let mut rows = rows.clone();
544 rows.shift_remove(key);
545 let segments = checked_segments(path)?;
546 let view = remove_row_view(&self.view, &row_path(&segments, key));
547 let history = self.recorded(None);
548 self.commit_view(put_at(&self.data, &segments, rows.into()), view, history)
549 }
550
551 pub fn move_row(&mut self, path: &str, key: &str, index: i64) -> FormResult<()> {
554 let (_, rows) = self.editable_collection(path)?;
555 let mut entries = rows
556 .iter()
557 .map(|(k, v)| (k.clone(), v.clone()))
558 .collect::<Vec<_>>();
559 let from = entries
560 .iter()
561 .position(|(k, _)| k == key)
562 .ok_or_else(|| FormError::input(format!("Unknown row: {key}")))?;
563 if index < 0 || index >= entries.len() as i64 {
564 return Err(FormError::input(format!("Invalid row position: {index}")));
565 }
566 let to = index as usize;
567 if from == to {
568 return Ok(());
569 }
570 let row = entries.remove(from);
571 entries.insert(to, row);
572 self.commit(
573 put_at(
574 &self.data,
575 &checked_segments(path)?,
576 Value::Object(entries.into_iter().collect()),
577 ),
578 None,
579 )
580 }
581
582 pub fn rekey_row(&mut self, path: &str, old_key: &str, new_key: &str) -> FormResult<()> {
584 let (_, rows) = self.editable_collection(path)?;
585 check_key(new_key)?;
586 if !rows.contains_key(old_key) {
587 return Err(FormError::input(format!("Unknown row: {old_key}")));
588 }
589 if old_key == new_key {
590 return Ok(());
591 }
592 if rows.contains_key(new_key) {
593 return Err(FormError::input(format!(
594 "Row key already exists: {new_key}"
595 )));
596 }
597 let rows = rows
598 .iter()
599 .map(|(k, v)| {
600 (
601 if k == old_key {
602 new_key.to_owned()
603 } else {
604 k.clone()
605 },
606 v.clone(),
607 )
608 })
609 .collect::<Map<_, _>>();
610 let segments = checked_segments(path)?;
611 let view = rekey_row_view(
612 &self.view,
613 &row_path(&segments, old_key),
614 &row_path(&segments, new_key),
615 );
616 let history = self.recorded(None);
617 self.commit_view(put_at(&self.data, &segments, rows.into()), view, history)
618 }
619}