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