1use crate::coerce::coerce_value;
7use crate::types::{FieldValue, FormData};
8use indexmap::IndexMap;
9use xfa_layout_engine::form::{DrawContent, FormNodeId, FormNodeType, FormTree};
10
11pub fn form_tree_to_json(tree: &FormTree, root: FormNodeId) -> FormData {
17 let mut fields = IndexMap::new();
18 let node = tree.get(root);
19
20 match &node.node_type {
22 FormNodeType::Root | FormNodeType::PageSet | FormNodeType::PageArea { .. } => {
23 for &child_id in &node.children {
24 walk_node(tree, child_id, "", &mut fields);
25 }
26 }
27 _ => {
28 walk_node(tree, root, "", &mut fields);
29 }
30 }
31
32 FormData { fields }
33}
34
35pub fn form_tree_to_value(tree: &FormTree, root: FormNodeId) -> serde_json::Value {
39 let data = form_tree_to_json(tree, root);
40 serde_json::to_value(data).unwrap_or(serde_json::Value::Null)
41}
42
43fn walk_node(
45 tree: &FormTree,
46 node_id: FormNodeId,
47 parent_path: &str,
48 fields: &mut IndexMap<String, FieldValue>,
49) {
50 let node = tree.get(node_id);
51 let path = if parent_path.is_empty() {
52 node.name.clone()
53 } else {
54 format!("{}.{}", parent_path, node.name)
55 };
56
57 match &node.node_type {
58 FormNodeType::Field { value } => {
59 fields.insert(path, coerce_value(value));
60 }
61 FormNodeType::Draw(DrawContent::Text(content)) => {
62 if !content.is_empty() {
63 fields.insert(path, FieldValue::Text(content.clone()));
64 }
65 }
66 FormNodeType::Draw(_) | FormNodeType::Image { .. } => {
67 }
69 FormNodeType::Subform | FormNodeType::Area | FormNodeType::ExclGroup => {
72 if node.occur.is_repeating() {
73 let mut instance = IndexMap::new();
77 for &child_id in &node.children {
78 walk_node_into_map(tree, child_id, &mut instance);
79 }
80 match fields.get_mut(&path) {
82 Some(FieldValue::Array(arr)) => {
83 arr.push(instance);
84 }
85 _ => {
86 fields.insert(path, FieldValue::Array(vec![instance]));
87 }
88 }
89 } else {
90 for &child_id in &node.children {
92 walk_node(tree, child_id, &path, fields);
93 }
94 }
95 }
96 FormNodeType::SubformSet
97 | FormNodeType::Root
98 | FormNodeType::PageSet
99 | FormNodeType::PageArea { .. } => {
100 for &child_id in &node.children {
101 walk_node(tree, child_id, &path, fields);
102 }
103 }
104 }
105}
106
107fn walk_node_into_map(
109 tree: &FormTree,
110 node_id: FormNodeId,
111 map: &mut IndexMap<String, FieldValue>,
112) {
113 let node = tree.get(node_id);
114
115 match &node.node_type {
116 FormNodeType::Field { value } => {
117 map.insert(node.name.clone(), coerce_value(value));
118 }
119 FormNodeType::Draw(DrawContent::Text(content)) => {
120 if !content.is_empty() {
121 map.insert(node.name.clone(), FieldValue::Text(content.clone()));
122 }
123 }
124 FormNodeType::Draw(_) | FormNodeType::Image { .. } => {
125 }
127 FormNodeType::Subform => {
128 if node.occur.is_repeating() {
129 let mut instance = IndexMap::new();
130 for &child_id in &node.children {
131 walk_node_into_map(tree, child_id, &mut instance);
132 }
133 match map.get_mut(&node.name) {
134 Some(FieldValue::Array(arr)) => {
135 arr.push(instance);
136 }
137 _ => {
138 map.insert(node.name.clone(), FieldValue::Array(vec![instance]));
139 }
140 }
141 } else {
142 for &child_id in &node.children {
144 let child = tree.get(child_id);
145 let key = format!("{}.{}", node.name, child.name);
146 match &child.node_type {
147 FormNodeType::Field { value } => {
148 map.insert(key, coerce_value(value));
149 }
150 FormNodeType::Draw(DrawContent::Text(content)) => {
151 if !content.is_empty() {
152 map.insert(key, FieldValue::Text(content.clone()));
153 }
154 }
155 _ => {
156 walk_node_into_map(tree, child_id, map);
157 }
158 }
159 }
160 }
161 }
162 _ => {
163 for &child_id in &node.children {
164 walk_node_into_map(tree, child_id, map);
165 }
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use xfa_layout_engine::form::{FormNode, Occur};
174 use xfa_layout_engine::text::FontMetrics;
175 use xfa_layout_engine::types::{BoxModel, LayoutStrategy};
176
177 fn make_field(tree: &mut FormTree, name: &str, value: &str) -> FormNodeId {
178 tree.add_node(FormNode {
179 name: name.to_string(),
180 node_type: FormNodeType::Field {
181 value: value.to_string(),
182 },
183 box_model: BoxModel::default(),
184 layout: LayoutStrategy::Positioned,
185 children: vec![],
186 occur: Occur::once(),
187 font: FontMetrics::default(),
188 calculate: None,
189 validate: None,
190 column_widths: vec![],
191 col_span: 1,
192 })
193 }
194
195 fn make_subform(
196 tree: &mut FormTree,
197 name: &str,
198 children: Vec<FormNodeId>,
199 occur: Occur,
200 ) -> FormNodeId {
201 tree.add_node(FormNode {
202 name: name.to_string(),
203 node_type: FormNodeType::Subform,
204 box_model: BoxModel::default(),
205 layout: LayoutStrategy::TopToBottom,
206 children,
207 occur,
208 font: FontMetrics::default(),
209 calculate: None,
210 validate: None,
211 column_widths: vec![],
212 col_span: 1,
213 })
214 }
215
216 fn make_root(tree: &mut FormTree, children: Vec<FormNodeId>) -> FormNodeId {
217 tree.add_node(FormNode {
218 name: "Root".to_string(),
219 node_type: FormNodeType::Root,
220 box_model: BoxModel::default(),
221 layout: LayoutStrategy::TopToBottom,
222 children,
223 occur: Occur::once(),
224 font: FontMetrics::default(),
225 calculate: None,
226 validate: None,
227 column_widths: vec![],
228 col_span: 1,
229 })
230 }
231
232 #[test]
233 fn simple_form_export() {
234 let mut tree = FormTree::new();
235 let name = make_field(&mut tree, "Name", "Acme Corp");
236 let amount = make_field(&mut tree, "Amount", "42.50");
237 let active = make_field(&mut tree, "Active", "true");
238
239 let form = make_subform(
240 &mut tree,
241 "form1",
242 vec![name, amount, active],
243 Occur::once(),
244 );
245 let root = make_root(&mut tree, vec![form]);
246
247 let data = form_tree_to_json(&tree, root);
248
249 assert_eq!(
250 data.fields.get("form1.Name"),
251 Some(&FieldValue::Text("Acme Corp".to_string()))
252 );
253 assert_eq!(
254 data.fields.get("form1.Amount"),
255 Some(&FieldValue::Number(42.50))
256 );
257 assert_eq!(
258 data.fields.get("form1.Active"),
259 Some(&FieldValue::Boolean(true))
260 );
261 }
262
263 #[test]
264 fn nested_subforms() {
265 let mut tree = FormTree::new();
266 let street = make_field(&mut tree, "Street", "123 Main St");
267 let city = make_field(&mut tree, "City", "Springfield");
268
269 let address = make_subform(&mut tree, "Address", vec![street, city], Occur::once());
270 let form = make_subform(&mut tree, "form1", vec![address], Occur::once());
271 let root = make_root(&mut tree, vec![form]);
272
273 let data = form_tree_to_json(&tree, root);
274
275 assert_eq!(
276 data.fields.get("form1.Address.Street"),
277 Some(&FieldValue::Text("123 Main St".to_string()))
278 );
279 assert_eq!(
280 data.fields.get("form1.Address.City"),
281 Some(&FieldValue::Text("Springfield".to_string()))
282 );
283 }
284
285 #[test]
286 fn repeating_subforms_as_arrays() {
287 let mut tree = FormTree::new();
288
289 let desc1 = make_field(&mut tree, "Description", "Widget A");
290 let qty1 = make_field(&mut tree, "Qty", "10");
291 let item1 = make_subform(
292 &mut tree,
293 "Item",
294 vec![desc1, qty1],
295 Occur::repeating(0, None, 2),
296 );
297
298 let desc2 = make_field(&mut tree, "Description", "Widget B");
299 let qty2 = make_field(&mut tree, "Qty", "5");
300 let item2 = make_subform(
301 &mut tree,
302 "Item",
303 vec![desc2, qty2],
304 Occur::repeating(0, None, 2),
305 );
306
307 let form = make_subform(&mut tree, "form1", vec![item1, item2], Occur::once());
308 let root = make_root(&mut tree, vec![form]);
309
310 let data = form_tree_to_json(&tree, root);
311
312 let items = data.fields.get("form1.Item").unwrap();
313 match items {
314 FieldValue::Array(arr) => {
315 assert_eq!(arr.len(), 2);
316 assert_eq!(
317 arr[0].get("Description"),
318 Some(&FieldValue::Text("Widget A".to_string()))
319 );
320 assert_eq!(arr[0].get("Qty"), Some(&FieldValue::Number(10.0)));
321 assert_eq!(
322 arr[1].get("Description"),
323 Some(&FieldValue::Text("Widget B".to_string()))
324 );
325 assert_eq!(arr[1].get("Qty"), Some(&FieldValue::Number(5.0)));
326 }
327 _ => panic!("Expected Array, got {items:?}"),
328 }
329 }
330
331 #[test]
332 fn empty_fields_are_null() {
333 let mut tree = FormTree::new();
334 let empty = make_field(&mut tree, "Empty", "");
335 let form = make_subform(&mut tree, "form1", vec![empty], Occur::once());
336 let root = make_root(&mut tree, vec![form]);
337
338 let data = form_tree_to_json(&tree, root);
339 assert_eq!(data.fields.get("form1.Empty"), Some(&FieldValue::Null));
340 }
341
342 #[test]
343 fn form_tree_to_value_produces_valid_json() {
344 let mut tree = FormTree::new();
345 let name = make_field(&mut tree, "Name", "Test");
346 let form = make_subform(&mut tree, "form1", vec![name], Occur::once());
347 let root = make_root(&mut tree, vec![form]);
348
349 let value = form_tree_to_value(&tree, root);
350 assert!(value.is_object());
351
352 let fields = value.get("fields").unwrap();
353 assert_eq!(fields.get("form1.Name").unwrap(), "Test");
354 }
355
356 #[test]
357 fn type_coercion_in_export() {
358 let mut tree = FormTree::new();
359 let num = make_field(&mut tree, "Total", "112.50");
360 let flag = make_field(&mut tree, "Checked", "0");
361 let text = make_field(&mut tree, "Note", "See attached");
362 let null_field = make_field(&mut tree, "Blank", "");
363
364 let form = make_subform(
365 &mut tree,
366 "form1",
367 vec![num, flag, text, null_field],
368 Occur::once(),
369 );
370 let root = make_root(&mut tree, vec![form]);
371
372 let data = form_tree_to_json(&tree, root);
373
374 assert_eq!(
375 data.fields.get("form1.Total"),
376 Some(&FieldValue::Number(112.50))
377 );
378 assert_eq!(
379 data.fields.get("form1.Checked"),
380 Some(&FieldValue::Boolean(false))
381 );
382 assert_eq!(
383 data.fields.get("form1.Note"),
384 Some(&FieldValue::Text("See attached".to_string()))
385 );
386 assert_eq!(data.fields.get("form1.Blank"), Some(&FieldValue::Null));
387 }
388}