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 ) -> Result<crate::ValidationResult, String> {
582 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
583 if let Some(idx) = idx_opt {
584 let data_value = crate::jsoneval::json_parser::parse_json_str(data)
585 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
586 let context_value = if let Some(ctx) = context {
587 crate::jsoneval::json_parser::parse_json_str(ctx)
588 .map_err(|e| format!("Failed to parse subform context: {}", e))?
589 } else {
590 Value::Object(serde_json::Map::new())
591 };
592 let data_for_validation = data_value.clone();
593 self.with_item_cache_swap(
594 base_path.as_ref(),
595 idx,
596 data_value,
597 context_value,
598 move |sf| {
599 sf.evaluate_internal_pre_diffed(paths, token)?;
601 sf.validate_pre_set(data_for_validation, paths, token)
602 },
603 )
604 } else {
605 let subform = self
606 .subforms
607 .get_mut(base_path.as_ref() as &str)
608 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
609 subform.validate(data, context, paths, token)
610 }
611 }
612
613 pub fn evaluate_dependents_subform(
619 &mut self,
620 subform_path: &str,
621 changed_paths: &[String],
622 data: Option<&str>,
623 context: Option<&str>,
624 re_evaluate: bool,
625 token: Option<&CancellationToken>,
626 canceled_paths: Option<&mut Vec<String>>,
627 include_subforms: bool,
628 ) -> Result<Value, String> {
629 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
630 if let Some(idx) = idx_opt {
631 let (data_value, context_value) = if let Some(data_str) = data {
633 let dv = crate::jsoneval::json_parser::parse_json_str(data_str)
634 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
635 let cv = if let Some(ctx) = context {
636 crate::jsoneval::json_parser::parse_json_str(ctx)
637 .map_err(|e| format!("Failed to parse subform context: {}", e))?
638 } else {
639 Value::Object(serde_json::Map::new())
640 };
641 (dv, cv)
642 } else {
643 let subform = self
645 .subforms
646 .get(base_path.as_ref() as &str)
647 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
648 let dv = subform.eval_data.snapshot_data_clone();
649 (dv, Value::Object(serde_json::Map::new()))
650 };
651 let changes = self.with_item_cache_swap(
652 base_path.as_ref(),
653 idx,
654 data_value,
655 context_value,
656 |sf| {
657 sf.evaluate_dependents(
659 changed_paths,
660 None,
661 None,
662 re_evaluate,
663 token,
664 None,
665 include_subforms,
666 )
667 },
668 )?;
669 let subform_dot_path =
673 crate::jsoneval::path_utils::pointer_to_dot_notation(base_path.as_ref())
674 .replace(".properties.", ".");
675 let canonical_prefix = format!("{subform_dot_path}.{idx}");
676 let root_key = base_path.rsplit('/').next().unwrap_or(base_path.as_ref());
677 let changes = match changes {
678 Value::Array(changes) => Value::Array(
679 changes
680 .into_iter()
681 .map(|change| {
682 let Some(change_map) = change.as_object() else {
683 return change;
684 };
685 let Some(Value::String(reference)) = change_map.get("$ref") else {
686 return change;
687 };
688 let Some(suffix) = reference.strip_prefix(&canonical_prefix) else {
689 return change;
690 };
691 if !suffix.is_empty() && !suffix.starts_with('.') {
692 return change;
693 }
694 let mut mapped = change_map.clone();
695 mapped.insert(
696 "$ref".to_string(),
697 Value::String(format!("{root_key}{suffix}")),
698 );
699 Value::Object(mapped)
700 })
701 .collect(),
702 ),
703 value => value,
704 };
705 Ok(changes)
706 } else {
707 let subform = self
708 .subforms
709 .get_mut(base_path.as_ref() as &str)
710 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
711 subform.evaluate_dependents(
712 changed_paths,
713 data,
714 context,
715 re_evaluate,
716 token,
717 canceled_paths,
718 include_subforms,
719 )
720 }
721 }
722
723 pub fn resolve_layout_subform(
725 &mut self,
726 subform_path: &str,
727 evaluate: bool,
728 ) -> Result<ResolvedLayoutResult, String> {
729 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
730 let subform = self
731 .subforms
732 .get_mut(base_path.as_ref() as &str)
733 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
734 subform.resolve_layout(evaluate)
735 }
736
737 pub fn get_evaluated_schema_subform(&mut self, subform_path: &str) -> Value {
739 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
740
741 if let Some(idx) = idx_opt {
742 if let Some(schema) = self
743 .eval_cache
744 .subform_caches
745 .get(&idx)
746 .and_then(|c| c.evaluated_schema.clone())
747 {
748 return schema;
749 }
750 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
751 subform.get_evaluated_schema()
752 } else {
753 Value::Null
754 }
755 } else if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
756 subform.get_evaluated_schema()
757 } else {
758 Value::Null
759 }
760 }
761
762 pub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value {
768 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
769 let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) else {
770 return Value::Null;
771 };
772
773 let values = subform.get_schema_value();
774 let Some(values) = values.as_object() else {
775 return values;
776 };
777
778 let schema_root_keys: Vec<&str> = subform
779 .schema
780 .as_object()
781 .into_iter()
782 .flat_map(|schema| schema.keys())
783 .filter(|key| !key.starts_with('$'))
784 .map(String::as_str)
785 .collect();
786
787 Value::Object(
788 schema_root_keys
789 .into_iter()
790 .filter_map(|key| {
791 values
792 .get(key)
793 .cloned()
794 .map(|value| (key.to_string(), value))
795 })
796 .collect(),
797 )
798 }
799
800 pub fn get_schema_value_array_subform(&self, subform_path: &str) -> Value {
802 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
803 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
804 subform.get_schema_value_array()
805 } else {
806 Value::Array(vec![])
807 }
808 }
809
810 pub fn get_schema_value_object_subform(&self, subform_path: &str) -> Value {
812 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
813 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
814 subform.get_schema_value_object()
815 } else {
816 Value::Object(serde_json::Map::new())
817 }
818 }
819
820 pub fn get_evaluated_schema_without_params_subform(&mut self, subform_path: &str) -> Value {
822 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
823 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
824 subform.get_evaluated_schema_without_params()
825 } else {
826 Value::Null
827 }
828 }
829
830 pub fn get_evaluated_schema_by_path_subform(
832 &mut self,
833 subform_path: &str,
834 schema_path: &str,
835 ) -> Option<Value> {
836 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
837 self.subforms.get_mut(base_path.as_ref() as &str).map(|sf| {
838 sf.get_evaluated_schema_by_paths(&[schema_path.to_string()], Some(ReturnFormat::Nested))
839 })
840 }
841
842 pub fn get_evaluated_schema_by_paths_subform(
844 &mut self,
845 subform_path: &str,
846 schema_paths: &[String],
847 format: Option<crate::ReturnFormat>,
848 ) -> Value {
849 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
850 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
851 subform.get_evaluated_schema_by_paths(
852 schema_paths,
853 Some(format.unwrap_or(ReturnFormat::Flat)),
854 )
855 } else {
856 match format.unwrap_or_default() {
857 crate::ReturnFormat::Array => Value::Array(vec![]),
858 _ => Value::Object(serde_json::Map::new()),
859 }
860 }
861 }
862
863 pub fn get_schema_by_path_subform(
865 &self,
866 subform_path: &str,
867 schema_path: &str,
868 ) -> Option<Value> {
869 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
870 self.subforms
871 .get(base_path.as_ref() as &str)
872 .and_then(|sf| sf.get_schema_by_path(schema_path))
873 }
874
875 pub fn get_schema_by_paths_subform(
877 &self,
878 subform_path: &str,
879 schema_paths: &[String],
880 format: Option<crate::ReturnFormat>,
881 ) -> Value {
882 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
883 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
884 subform.get_schema_by_paths(schema_paths, Some(format.unwrap_or(ReturnFormat::Flat)))
885 } else {
886 match format.unwrap_or_default() {
887 crate::ReturnFormat::Array => Value::Array(vec![]),
888 _ => Value::Object(serde_json::Map::new()),
889 }
890 }
891 }
892
893 pub fn get_resolved_layout_subform(&mut self, subform_path: &str) -> ResolvedLayoutResult {
895 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
896 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
897 subform.get_resolved_layout()
898 } else {
899 ResolvedLayoutResult::default()
900 }
901 }
902
903 pub fn get_evaluated_schema_resolved_subform(&mut self, subform_path: &str) -> Value {
905 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
906 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
907 subform.get_evaluated_schema_resolved()
908 } else {
909 Value::Null
910 }
911 }
912
913 pub fn get_subform_paths(&self) -> Vec<String> {
915 self.subforms.keys().cloned().collect()
916 }
917
918 pub fn has_subform(&self, subform_path: &str) -> bool {
920 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
921 self.subforms.contains_key(base_path.as_ref() as &str)
922 }
923}