1use super::JSONEval;
4use crate::jsoneval::cancellation::CancellationToken;
5use crate::jsoneval::eval_data::EvalData;
6use crate::jsoneval::types::{ResolvedLayoutResult, ReturnFormat};
7use serde_json::Value;
8
9fn resolve_subform_path(path: &str) -> (String, Option<usize>) {
25 let path = path.trim_end_matches('.');
27 let (path, bracket_idx) = if let Some(bracket_start) = path.rfind('[') {
28 let after = &path[bracket_start + 1..];
29 if let Some(bracket_end) = after.find(']') {
30 let idx_str = &after[..bracket_end];
31 if let Ok(idx) = idx_str.parse::<usize>() {
32 let base = path[..bracket_start].trim_end_matches('.');
34 (base, Some(idx))
35 } else {
36 (path, None)
37 }
38 } else {
39 (path, None)
40 }
41 } else {
42 (path, None)
43 };
44
45 let (base_raw, trailing_idx) = if bracket_idx.is_none() {
47 if let Some(dot_pos) = path.rfind('.') {
49 let suffix = &path[dot_pos + 1..];
50 if let Ok(idx) = suffix.parse::<usize>() {
51 (&path[..dot_pos], Some(idx))
52 } else {
53 (path, None)
54 }
55 }
56 else if let Some(slash_pos) = path.rfind('/') {
58 let suffix = &path[slash_pos + 1..];
59 if let Ok(idx) = suffix.parse::<usize>() {
60 (&path[..slash_pos], Some(idx))
61 } else {
62 (path, None)
63 }
64 } else {
65 (path, None)
66 }
67 } else {
68 (path, None)
69 };
70
71 let final_idx = bracket_idx.or(trailing_idx);
72
73 let canonical = normalize_to_subform_key(base_raw);
75
76 (canonical, final_idx)
77}
78
79fn normalize_to_subform_key(path: &str) -> String {
85 if path.starts_with("#/") {
87 return path.to_string();
88 }
89
90 if path.starts_with('/') {
92 return format!("#{}", path);
93 }
94
95 crate::jsoneval::path_utils::dot_notation_to_schema_pointer(path)
98}
99
100impl JSONEval {
101 pub(crate) fn resolve_subform_path_alias(&self, path: &str) -> (String, Option<usize>) {
105 let (mut canonical, idx) = resolve_subform_path(path);
106
107 if !self.subforms.contains_key(&canonical) {
108 let search_suffix = if canonical.starts_with("#/") {
109 format!("/properties/{}", &canonical[2..])
110 } else {
111 format!("/properties/{}", canonical)
112 };
113
114 for k in self.subforms.keys() {
115 if k.ends_with(&search_suffix) || k == &canonical {
116 canonical = k.to_string();
117 break;
118 }
119 }
120 }
121
122 (canonical, idx)
123 }
124
125 fn with_item_cache_swap<F, T>(
137 &mut self,
138 base_path: &str,
139 idx: usize,
140 data_value: Value,
141 context_value: Value,
142 f: F,
143 ) -> Result<T, String>
144 where
145 F: FnOnce(&mut JSONEval) -> Result<T, String>,
146 {
147 let original_field_key = base_path
148 .split('/')
149 .next_back()
150 .unwrap_or(base_path)
151 .to_string();
152
153 let schema_pointer = if base_path.starts_with("#/") {
154 &base_path[1..]
155 } else if base_path.starts_with('#') {
156 &base_path[1..]
157 } else {
158 base_path
159 };
160
161 let root_key =
162 crate::jsoneval::path_utils::get_value_by_pointer(&self.schema, schema_pointer)
163 .and_then(|node| node.get("itemsRootKey"))
164 .and_then(|v| v.as_str())
165 .unwrap_or(&original_field_key)
166 .to_string();
167
168 let array_path =
169 crate::jsoneval::path_utils::schema_path_to_data_pointer(base_path).into_owned();
170 let item_path = format!("{}/{}", array_path, idx);
171 let full_parent_payload = data_value.pointer(&array_path).is_some();
172 let payload_has_parent_context = data_value
173 .as_object()
174 .map(|map| map.keys().any(|key| key != &root_key))
175 .unwrap_or(false);
176
177 let normalized_item = if full_parent_payload {
179 data_value
180 .pointer(&item_path)
181 .cloned()
182 .or_else(|| data_value.get(&root_key).cloned())
183 } else {
184 data_value.get(&root_key).cloned()
185 }
186 .ok_or_else(|| {
187 format!(
188 "Invalid indexed subform payload for {base_path}[{idx}]: expected active item at {item_path} or wrapper root {root_key}"
189 )
190 })?;
191
192 let (old_item_snapshot, new_item_val, subform_item_cache_opt) = {
194 let subform = self
195 .subforms
196 .get_mut(base_path)
197 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
198
199 let old_item_snapshot = subform
200 .eval_cache
201 .subform_caches
202 .get(&idx)
203 .map(|c| c.item_snapshot.clone())
204 .unwrap_or(Value::Null);
205
206 let mut scoped_data = EvalData::new(self.eval_data.snapshot_data_clone());
208 if full_parent_payload || payload_has_parent_context {
209 scoped_data.replace_data_and_context(data_value.clone(), context_value.clone());
210 }
211 scoped_data.set(&item_path, normalized_item.clone());
212 let canonical_parent = scoped_data.snapshot_data_clone();
213 let scope = crate::jsoneval::subform_scope::SubformScope::new(
214 base_path,
215 &array_path,
216 Some(idx),
217 );
218 let mut scoped_view = scope.evaluation_view(&canonical_parent);
219 if let Some(view) = scoped_view.as_object_mut() {
220 view.insert("$context".to_string(), context_value.clone());
221 }
222 subform.eval_data = EvalData::new(scoped_view);
223 let new_item_val = normalized_item.clone();
224
225 let existing = subform.eval_cache.subform_caches.remove(&idx);
227 (old_item_snapshot, new_item_val, existing)
228 }; let parent_item = self.eval_data.get(&item_path).cloned();
232 let old_item_snapshot = if old_item_snapshot == Value::Null {
233 parent_item.clone().unwrap_or(Value::Null)
234 } else {
235 old_item_snapshot
236 };
237
238 let is_new_item = parent_item.is_none();
240
241 let mut parent_cache = std::mem::take(&mut self.eval_cache);
242 if full_parent_payload {
243 let old_parent_data = self.eval_data.snapshot_data_clone();
244 self.eval_data
245 .replace_data_and_context(data_value.clone(), context_value.clone());
246 let new_parent_data = self.eval_data.snapshot_data_clone();
247 crate::jsoneval::eval_cache::diff_and_update_versions(
248 &mut parent_cache.data_versions,
249 "",
250 &old_parent_data,
251 &new_parent_data,
252 "sync_full_subform_payload",
253 );
254 }
255 parent_cache.ensure_active_item_cache(idx);
256
257 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
258 c.data_versions
260 .merge_excluding_prefix(&parent_cache.data_versions, &format!("/{root_key}/"));
261 c.data_versions
262 .merge_from_params(&parent_cache.params_versions);
263
264 if let Some(subform_item_cache) = &subform_item_cache_opt {
266 c.data_versions
267 .merge_from(&subform_item_cache.data_versions);
268 }
269 }
270
271 let pre_diff_item_versions = parent_cache
273 .subform_caches
274 .get(&idx)
275 .map(|c| c.data_versions.clone());
276
277 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
278 crate::jsoneval::eval_cache::diff_and_update_versions(
280 &mut c.data_versions,
281 &format!("/{}", root_key),
282 &old_item_snapshot,
283 &new_item_val,
284 "with_item_cache_swap_diff_and_update_versions",
285 );
286 c.item_snapshot = new_item_val.clone();
287 }
288
289 {
291 let item_field_prefix = format!("/{}/", root_key);
292 if let (Some(ref pre), Some(c)) = (
293 &pre_diff_item_versions,
294 parent_cache.subform_caches.get(&idx),
295 ) {
296 let newly_bumped: Vec<String> = c
297 .data_versions
298 .versions()
299 .filter(|(k, &v)| k.starts_with(&item_field_prefix) && v > pre.get(k))
300 .map(|(k, _)| k.to_string())
301 .collect();
302 if !newly_bumped.is_empty() {
303 for k in newly_bumped {
304 parent_cache
305 .data_versions
306 .bump(&k, "propagate_newly_bumped");
307 }
308 parent_cache.eval_generation += 1;
309 }
310 }
311 }
312
313 parent_cache.active_item_index = Some(idx);
314
315 if let Some(subform_item_cache) = subform_item_cache_opt {
317 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
318 let current_dv = c.data_versions.clone();
320 for (k, v) in subform_item_cache.entries {
321 if c.entries.contains_key(&k) {
323 continue;
324 }
325 let still_valid = v.dep_versions.iter().all(|(dep_path, &cached_ver)| {
327 let current_ver = if dep_path.starts_with("/$params") {
328 parent_cache.params_versions.get(dep_path)
329 } else {
330 current_dv.get(dep_path)
331 };
332 current_ver == cached_ver
333 });
334 if still_valid {
335 c.entries.insert(k, v);
336 }
337 }
338 }
339 }
340
341 let current_at_item_path = self.eval_data.get(&item_path).cloned();
343 if current_at_item_path.as_ref() != Some(&new_item_val) {
344 self.eval_data.set(&item_path, new_item_val.clone());
345 if is_new_item {
346 parent_cache.bump_data_version(&array_path);
347 }
348 }
349
350 let field_prefix = format!("/{}/", root_key);
352 let item_paths_bumped = match &pre_diff_item_versions {
353 None => {
354 parent_cache
356 .subform_caches
357 .get(&idx)
358 .map(|c| c.data_versions.any_bumped_with_prefix(&field_prefix))
359 .unwrap_or(false)
360 }
361 Some(pre) => {
362 parent_cache
364 .subform_caches
365 .get(&idx)
366 .map(|c| {
367 c.data_versions
368 .any_newly_bumped_with_prefix(&field_prefix, pre)
369 })
370 .unwrap_or(false)
371 }
372 };
373
374 if is_new_item || item_paths_bumped {
375 let newly_bumped_paths: Option<Vec<String>> = if item_paths_bumped {
377 let paths = pre_diff_item_versions.as_ref().and_then(|pre| {
378 parent_cache.subform_caches.get(&idx).map(|c| {
379 c.data_versions
380 .versions()
381 .filter(|(k, &v)| k.starts_with(&field_prefix) && v > pre.get(k))
382 .map(|(k, _)| {
383 let sub = k.trim_start_matches(&field_prefix);
385 format!("#/{}/properties/{}", root_key, sub)
386 })
387 .collect::<Vec<_>>()
388 })
389 });
390 paths
391 } else {
392 None
393 };
394
395 let params_table_keys: Vec<String> = self
396 .table_metadata
397 .keys()
398 .filter(|k| k.starts_with("#/$params"))
399 .filter(|k| {
400 if is_new_item {
401 return true; }
403 let Some(ref bumped) = newly_bumped_paths else {
405 return true;
406 };
407 if bumped.is_empty() {
408 return false;
409 }
410 self.dependencies
411 .get(*k)
412 .map(|deps| {
413 deps.iter().any(|dep| {
414 bumped
415 .iter()
416 .any(|b| dep == b || dep.starts_with(b.as_str()))
417 })
418 })
419 .unwrap_or(false)
420 })
421 .cloned()
422 .collect();
423 if !params_table_keys.is_empty() {
424 parent_cache.invalidate_params_tables_for_item(idx, ¶ms_table_keys);
425
426 let eval_data_snapshot = self.eval_data.snapshot_data();
427 for key in ¶ms_table_keys {
428 let depends_on_subform_item = if let Some(deps) = self.dependencies.get(key) {
430 let subform_dep_prefix = format!("#/{}/properties/", root_key);
431 let subform_dep_prefix_short = format!("#/{}/", root_key);
432 deps.iter().any(|dep| {
433 dep.starts_with(&subform_dep_prefix)
434 || dep.starts_with(&subform_dep_prefix_short)
435 })
436 } else {
437 false
438 };
439
440 if depends_on_subform_item {
441 continue;
442 }
443
444 if let Ok((arc_val, external_deps_opt)) =
446 crate::jsoneval::table_evaluate::evaluate_table(
447 self,
448 key,
449 &EvalData::from_arc(std::sync::Arc::clone(&eval_data_snapshot)),
450 None,
451 )
452 {
453 if crate::utils::is_debug_cache_enabled() {
454 let rows_len = arc_val.as_array().map(|a| a.len()).unwrap_or(0);
455 println!("PARENT EVALUATED TABLE {} -> {} rows", key, rows_len);
456 }
457
458 if let Some(external_deps) = external_deps_opt {
459 parent_cache.active_item_index = None;
461 parent_cache.store_cache_arc(key, &external_deps, arc_val);
462 parent_cache.active_item_index = Some(idx);
463 }
464 } else {
465 if crate::utils::is_debug_cache_enabled() {
466 println!("PARENT EVALUATED TABLE {} -> ERROR", key);
467 }
468 }
469 }
470 }
471 }
472
473 {
475 let subform = self.subforms.get_mut(base_path).unwrap();
476 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
477 }
478
479 let result = {
481 let subform = self.subforms.get_mut(base_path).unwrap();
482 f(subform)
483 };
484
485 {
487 let subform = self.subforms.get_mut(base_path).unwrap();
488 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
489 }
490 parent_cache.active_item_index = None;
491 self.eval_cache = parent_cache;
492
493 {
495 let subform = self.subforms.get_mut(base_path).unwrap();
496 if let Some(item_cache) = self.eval_cache.subform_caches.get_mut(&idx) {
497 item_cache.evaluated_schema = Some(subform.evaluated_schema.clone());
498 subform
499 .eval_cache
500 .subform_caches
501 .insert(idx, item_cache.clone());
502 }
503 }
504
505 result
506 }
507
508 pub fn evaluate_subform(
520 &mut self,
521 subform_path: &str,
522 data: &str,
523 context: Option<&str>,
524 paths: Option<&[String]>,
525 token: Option<&CancellationToken>,
526 ) -> Result<(), String> {
527 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
528 if let Some(idx) = idx_opt {
529 self.evaluate_subform_item(&base_path, idx, data, context, paths, token)
530 } else {
531 let subform = self
532 .subforms
533 .get_mut(base_path.as_ref() as &str)
534 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
535 subform.evaluate(data, context, paths, token)
536 }
537 }
538
539 fn evaluate_subform_item(
541 &mut self,
542 base_path: &str,
543 idx: usize,
544 data: &str,
545 context: Option<&str>,
546 paths: Option<&[String]>,
547 token: Option<&CancellationToken>,
548 ) -> Result<(), String> {
549 let data_value = crate::jsoneval::json_parser::parse_json_str(data)
550 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
551 let context_value = if let Some(ctx) = context {
552 crate::jsoneval::json_parser::parse_json_str(ctx)
553 .map_err(|e| format!("Failed to parse subform context: {}", e))?
554 } else {
555 Value::Object(serde_json::Map::new())
556 };
557
558 self.with_item_cache_swap(base_path, idx, data_value, context_value, |sf| {
559 sf.evaluate_internal_pre_diffed(paths, token)?;
562 if sf.apply_visible_static_defaults_with_dependents(token)? {
563 sf.evaluate_internal_pre_diffed(paths, token)?;
564 }
565 Ok(())
566 })
567 }
568
569 pub fn validate_subform(
575 &mut self,
576 subform_path: &str,
577 data: &str,
578 context: Option<&str>,
579 paths: Option<&[String]>,
580 token: Option<&CancellationToken>,
581 validate_readonly: Option<bool>,
582 ) -> Result<crate::ValidationResult, String> {
583 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
584 if let Some(idx) = idx_opt {
585 let data_value = crate::jsoneval::json_parser::parse_json_str(data)
586 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
587 let context_value = if let Some(ctx) = context {
588 crate::jsoneval::json_parser::parse_json_str(ctx)
589 .map_err(|e| format!("Failed to parse subform context: {}", e))?
590 } else {
591 Value::Object(serde_json::Map::new())
592 };
593 let data_for_validation = data_value.clone();
594 self.with_item_cache_swap(
595 base_path.as_ref(),
596 idx,
597 data_value,
598 context_value,
599 move |sf| {
600 sf.evaluate_internal_pre_diffed(paths, token)?;
602 sf.validate_pre_set(data_for_validation, paths, token, validate_readonly)
603 },
604 )
605 } else {
606 let subform = self
607 .subforms
608 .get_mut(base_path.as_ref() as &str)
609 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
610 subform.validate(data, context, paths, token, validate_readonly)
611 }
612 }
613
614 pub fn evaluate_dependents_subform(
620 &mut self,
621 subform_path: &str,
622 changed_paths: &[String],
623 data: Option<&str>,
624 context: Option<&str>,
625 re_evaluate: bool,
626 token: Option<&CancellationToken>,
627 canceled_paths: Option<&mut Vec<String>>,
628 include_subforms: bool,
629 ) -> Result<Value, String> {
630 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
631 if let Some(idx) = idx_opt {
632 let (data_value, context_value) = if let Some(data_str) = data {
634 let dv = crate::jsoneval::json_parser::parse_json_str(data_str)
635 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
636 let cv = if let Some(ctx) = context {
637 crate::jsoneval::json_parser::parse_json_str(ctx)
638 .map_err(|e| format!("Failed to parse subform context: {}", e))?
639 } else {
640 Value::Object(serde_json::Map::new())
641 };
642 (dv, cv)
643 } else {
644 let subform = self
646 .subforms
647 .get(base_path.as_ref() as &str)
648 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
649 let dv = subform.eval_data.snapshot_data_clone();
650 (dv, Value::Object(serde_json::Map::new()))
651 };
652 let changes = self.with_item_cache_swap(
653 base_path.as_ref(),
654 idx,
655 data_value,
656 context_value,
657 |sf| {
658 sf.evaluate_dependents(
660 changed_paths,
661 None,
662 None,
663 re_evaluate,
664 token,
665 None,
666 include_subforms,
667 )
668 },
669 )?;
670 let subform_dot_path =
674 crate::jsoneval::path_utils::pointer_to_dot_notation(base_path.as_ref())
675 .replace(".properties.", ".");
676 let canonical_prefix = format!("{subform_dot_path}.{idx}");
677 let root_key = base_path.rsplit('/').next().unwrap_or(base_path.as_ref());
678 let changes = match changes {
679 Value::Array(changes) => Value::Array(
680 changes
681 .into_iter()
682 .map(|change| {
683 let Some(change_map) = change.as_object() else {
684 return change;
685 };
686 let Some(Value::String(reference)) = change_map.get("$ref") else {
687 return change;
688 };
689 let Some(suffix) = reference.strip_prefix(&canonical_prefix) else {
690 return change;
691 };
692 if !suffix.is_empty() && !suffix.starts_with('.') {
693 return change;
694 }
695 let mut mapped = change_map.clone();
696 mapped.insert(
697 "$ref".to_string(),
698 Value::String(format!("{root_key}{suffix}")),
699 );
700 Value::Object(mapped)
701 })
702 .collect(),
703 ),
704 value => value,
705 };
706 Ok(changes)
707 } else {
708 let subform = self
709 .subforms
710 .get_mut(base_path.as_ref() as &str)
711 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
712 subform.evaluate_dependents(
713 changed_paths,
714 data,
715 context,
716 re_evaluate,
717 token,
718 canceled_paths,
719 include_subforms,
720 )
721 }
722 }
723
724 pub fn resolve_layout_subform(
726 &mut self,
727 subform_path: &str,
728 evaluate: bool,
729 ) -> Result<ResolvedLayoutResult, String> {
730 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
731 let subform = self
732 .subforms
733 .get_mut(base_path.as_ref() as &str)
734 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
735 subform.resolve_layout(evaluate)
736 }
737
738 pub fn get_evaluated_schema_subform(&mut self, subform_path: &str) -> Value {
740 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
741
742 if let Some(idx) = idx_opt {
743 if let Some(schema) = self
744 .eval_cache
745 .subform_caches
746 .get(&idx)
747 .and_then(|c| c.evaluated_schema.clone())
748 {
749 return schema;
750 }
751 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
752 subform.get_evaluated_schema()
753 } else {
754 Value::Null
755 }
756 } else if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
757 subform.get_evaluated_schema()
758 } else {
759 Value::Null
760 }
761 }
762
763 pub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value {
769 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
770 let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) else {
771 return Value::Null;
772 };
773
774 let values = subform.get_schema_value();
775 let Some(values) = values.as_object() else {
776 return values;
777 };
778
779 let schema_root_keys: Vec<&str> = subform
780 .schema
781 .as_object()
782 .into_iter()
783 .flat_map(|schema| schema.keys())
784 .filter(|key| !key.starts_with('$'))
785 .map(String::as_str)
786 .collect();
787
788 Value::Object(
789 schema_root_keys
790 .into_iter()
791 .filter_map(|key| {
792 values
793 .get(key)
794 .cloned()
795 .map(|value| (key.to_string(), value))
796 })
797 .collect(),
798 )
799 }
800
801 pub fn get_schema_value_array_subform(&self, subform_path: &str) -> Value {
803 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
804 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
805 subform.get_schema_value_array()
806 } else {
807 Value::Array(vec![])
808 }
809 }
810
811 pub fn get_schema_value_object_subform(&self, subform_path: &str) -> Value {
813 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
814 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
815 subform.get_schema_value_object()
816 } else {
817 Value::Object(serde_json::Map::new())
818 }
819 }
820
821 pub fn get_evaluated_schema_without_params_subform(&mut self, subform_path: &str) -> Value {
823 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
824 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
825 subform.get_evaluated_schema_without_params()
826 } else {
827 Value::Null
828 }
829 }
830
831 pub fn get_evaluated_schema_by_path_subform(
833 &mut self,
834 subform_path: &str,
835 schema_path: &str,
836 ) -> Option<Value> {
837 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
838 self.subforms.get_mut(base_path.as_ref() as &str).map(|sf| {
839 sf.get_evaluated_schema_by_paths(&[schema_path.to_string()], Some(ReturnFormat::Nested))
840 })
841 }
842
843 pub fn get_evaluated_schema_by_paths_subform(
845 &mut self,
846 subform_path: &str,
847 schema_paths: &[String],
848 format: Option<crate::ReturnFormat>,
849 ) -> Value {
850 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
851 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
852 subform.get_evaluated_schema_by_paths(
853 schema_paths,
854 Some(format.unwrap_or(ReturnFormat::Flat)),
855 )
856 } else {
857 match format.unwrap_or_default() {
858 crate::ReturnFormat::Array => Value::Array(vec![]),
859 _ => Value::Object(serde_json::Map::new()),
860 }
861 }
862 }
863
864 pub fn get_schema_by_path_subform(
866 &self,
867 subform_path: &str,
868 schema_path: &str,
869 ) -> Option<Value> {
870 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
871 self.subforms
872 .get(base_path.as_ref() as &str)
873 .and_then(|sf| sf.get_schema_by_path(schema_path))
874 }
875
876 pub fn get_schema_by_paths_subform(
878 &self,
879 subform_path: &str,
880 schema_paths: &[String],
881 format: Option<crate::ReturnFormat>,
882 ) -> Value {
883 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
884 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
885 subform.get_schema_by_paths(schema_paths, Some(format.unwrap_or(ReturnFormat::Flat)))
886 } else {
887 match format.unwrap_or_default() {
888 crate::ReturnFormat::Array => Value::Array(vec![]),
889 _ => Value::Object(serde_json::Map::new()),
890 }
891 }
892 }
893
894 pub fn get_resolved_layout_subform(&mut self, subform_path: &str) -> ResolvedLayoutResult {
896 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
897 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
898 subform.get_resolved_layout()
899 } else {
900 ResolvedLayoutResult::default()
901 }
902 }
903
904 pub fn get_evaluated_schema_resolved_subform(&mut self, subform_path: &str) -> Value {
906 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
907 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
908 subform.get_evaluated_schema_resolved()
909 } else {
910 Value::Null
911 }
912 }
913
914 pub fn get_subform_paths(&self) -> Vec<String> {
916 self.subforms.keys().cloned().collect()
917 }
918
919 pub fn has_subform(&self, subform_path: &str) -> bool {
921 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
922 self.subforms.contains_key(base_path.as_ref() as &str)
923 }
924}