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 pub(crate) 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 persist_evaluated_schema: bool,
143 f: F,
144 ) -> Result<T, String>
145 where
146 F: FnOnce(&mut JSONEval) -> Result<T, String>,
147 {
148 let original_field_key = base_path
149 .split('/')
150 .next_back()
151 .unwrap_or(base_path)
152 .to_string();
153
154 let schema_pointer = if base_path.starts_with("#/") {
155 &base_path[1..]
156 } else if base_path.starts_with('#') {
157 &base_path[1..]
158 } else {
159 base_path
160 };
161
162 let root_key =
163 crate::jsoneval::path_utils::get_value_by_pointer(&self.schema, schema_pointer)
164 .and_then(|node| node.get("itemsRootKey"))
165 .and_then(|v| v.as_str())
166 .unwrap_or(&original_field_key)
167 .to_string();
168
169 let array_path =
170 crate::jsoneval::path_utils::schema_path_to_data_pointer(base_path).into_owned();
171 let item_path = format!("{}/{}", array_path, idx);
172 let full_parent_payload = data_value.pointer(&array_path).is_some();
173 let payload_has_parent_context = data_value
174 .as_object()
175 .map(|map| map.keys().any(|key| key != &root_key))
176 .unwrap_or(false);
177
178 let normalized_item = if full_parent_payload {
180 data_value
181 .pointer(&item_path)
182 .cloned()
183 .or_else(|| data_value.get(&root_key).cloned())
184 } else {
185 data_value.get(&root_key).cloned()
186 }
187 .ok_or_else(|| {
188 format!(
189 "Invalid indexed subform payload for {base_path}[{idx}]: expected active item at {item_path} or wrapper root {root_key}"
190 )
191 })?;
192
193 let (old_item_snapshot, new_item_val, subform_item_cache_opt) = {
195 let subform = self
196 .subforms
197 .get_mut(base_path)
198 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
199
200 let old_item_snapshot = subform
201 .eval_cache
202 .subform_caches
203 .get(&idx)
204 .map(|c| c.item_snapshot.clone())
205 .unwrap_or(Value::Null);
206
207 let scope = crate::jsoneval::subform_scope::SubformScope::new(
208 base_path,
209 &array_path,
210 Some(idx),
211 );
212
213 let mut scoped_view = if self.eval_data.get(&item_path) == Some(&normalized_item) {
214 scope.evaluation_view(self.eval_data.data())
215 } else {
216 let mut scoped_data = self.eval_data.snapshot_data_clone();
217 if full_parent_payload || payload_has_parent_context {
218 if let Some(obj) = scoped_data.as_object_mut() {
219 if let Some(input_obj) = data_value.as_object() {
220 for (k, v) in input_obj {
221 obj.insert(k.clone(), v.clone());
222 }
223 }
224 }
225 }
226 crate::jsoneval::eval_data::EvalData::set_by_pointer(
227 &mut scoped_data,
228 &item_path,
229 normalized_item.clone(),
230 );
231 scope.evaluation_view(&scoped_data)
232 };
233
234 if let Some(view) = scoped_view.as_object_mut() {
235 view.insert("$context".to_string(), context_value.clone());
236 }
237 subform.eval_data = EvalData::new(scoped_view);
238 let new_item_val = normalized_item.clone();
239
240 let existing = subform.eval_cache.subform_caches.remove(&idx);
242 (old_item_snapshot, new_item_val, existing)
243 }; let parent_item = self.eval_data.get(&item_path).cloned();
247 let old_item_snapshot = if old_item_snapshot == Value::Null {
248 parent_item.clone().unwrap_or(Value::Null)
249 } else {
250 old_item_snapshot
251 };
252
253 let is_new_item = parent_item.is_none();
255
256 let mut parent_cache = std::mem::take(&mut self.eval_cache);
257 if full_parent_payload {
258 let needs_parent_sync = if let Some(input_obj) = data_value.as_object() {
259 let current = self.eval_data.data();
260 input_obj.iter().any(|(k, v)| current.get(k) != Some(v))
261 || current.get("$context") != Some(&context_value)
262 } else {
263 *self.eval_data.snapshot_data() != data_value
264 };
265 if needs_parent_sync {
266 let old_parent_data = self.eval_data.snapshot_data();
267 self.eval_data
268 .replace_data_and_context(data_value.clone(), context_value.clone());
269 let new_parent_data = self.eval_data.snapshot_data();
270 crate::jsoneval::eval_cache::diff_and_update_versions(
271 &mut parent_cache.data_versions,
272 "",
273 &old_parent_data,
274 &new_parent_data,
275 "sync_full_subform_payload",
276 );
277 }
278 }
279
280 parent_cache.ensure_active_item_cache(idx);
281
282 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
283 c.data_versions
285 .merge_excluding_prefix(&parent_cache.data_versions, &format!("/{root_key}/"));
286 c.data_versions
287 .merge_from_params(&parent_cache.params_versions);
288
289 if let Some(subform_item_cache) = &subform_item_cache_opt {
291 c.data_versions
292 .merge_from(&subform_item_cache.data_versions);
293 }
294 }
295
296 let pre_diff_item_versions = parent_cache
298 .subform_caches
299 .get(&idx)
300 .map(|c| c.data_versions.clone());
301
302 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
303 crate::jsoneval::eval_cache::diff_and_update_versions(
305 &mut c.data_versions,
306 &format!("/{}", root_key),
307 &old_item_snapshot,
308 &new_item_val,
309 "with_item_cache_swap_diff_and_update_versions",
310 );
311 c.item_snapshot = new_item_val.clone();
312 }
313
314 {
316 let item_field_prefix = format!("/{}/", root_key);
317 if let (Some(ref pre), Some(c)) = (
318 &pre_diff_item_versions,
319 parent_cache.subform_caches.get(&idx),
320 ) {
321 let newly_bumped: Vec<String> = c
322 .data_versions
323 .versions()
324 .filter(|(k, &v)| k.starts_with(&item_field_prefix) && v > pre.get(k))
325 .map(|(k, _)| k.to_string())
326 .collect();
327 if !newly_bumped.is_empty() {
328 for k in newly_bumped {
329 parent_cache
330 .data_versions
331 .bump(&k, "propagate_newly_bumped");
332 }
333 parent_cache.eval_generation += 1;
334 }
335 }
336 }
337
338 parent_cache.active_item_index = Some(idx);
339
340 if let Some(subform_item_cache) = subform_item_cache_opt {
342 if let Some(c) = parent_cache.subform_caches.get_mut(&idx) {
343 let current_dv = c.data_versions.clone();
345 for (k, v) in subform_item_cache.entries {
346 if c.entries.contains_key(&k) {
348 continue;
349 }
350 let still_valid =
352 v.dep_versions
353 .iter()
354 .all(|(dep_path, &cached_ver): (&String, &u64)| {
355 let current_ver = if dep_path.starts_with("/$params") {
356 parent_cache.params_versions.get(dep_path)
357 } else {
358 current_dv.get(dep_path)
359 };
360 current_ver == cached_ver
361 });
362 if still_valid {
363 c.entries.insert(k, v);
364 }
365 }
366 }
367 }
368
369 let current_at_item_path = self.eval_data.get(&item_path).cloned();
371 if current_at_item_path.as_ref() != Some(&new_item_val) {
372 self.eval_data.set(&item_path, new_item_val.clone());
373 if is_new_item {
374 parent_cache.bump_data_version(&array_path);
375 }
376 }
377
378 let field_prefix = format!("/{}/", root_key);
380 let item_paths_bumped = match &pre_diff_item_versions {
381 None => {
382 parent_cache
384 .subform_caches
385 .get(&idx)
386 .map(|c| c.data_versions.any_bumped_with_prefix(&field_prefix))
387 .unwrap_or(false)
388 }
389 Some(pre) => {
390 parent_cache
392 .subform_caches
393 .get(&idx)
394 .map(|c| {
395 c.data_versions
396 .any_newly_bumped_with_prefix(&field_prefix, pre)
397 })
398 .unwrap_or(false)
399 }
400 };
401
402 if is_new_item || item_paths_bumped {
403 let newly_bumped_paths: Option<Vec<String>> = if item_paths_bumped {
405 let paths = pre_diff_item_versions.as_ref().and_then(|pre| {
406 parent_cache.subform_caches.get(&idx).map(|c| {
407 c.data_versions
408 .versions()
409 .filter(|(k, &v)| k.starts_with(&field_prefix) && v > pre.get(k))
410 .map(|(k, _)| {
411 let sub = k.trim_start_matches(&field_prefix);
413 format!("#/{}/properties/{}", root_key, sub)
414 })
415 .collect::<Vec<_>>()
416 })
417 });
418 paths
419 } else {
420 None
421 };
422
423 let params_table_keys: Vec<String> = self
424 .table_metadata
425 .keys()
426 .filter(|k| k.starts_with("#/$params"))
427 .filter(|k| {
428 if is_new_item {
429 return true; }
431 let Some(ref bumped) = newly_bumped_paths else {
433 return true;
434 };
435 if bumped.is_empty() {
436 return false;
437 }
438 self.dependencies
439 .get(*k)
440 .map(|deps| {
441 deps.iter().any(|dep| {
442 bumped
443 .iter()
444 .any(|b| dep == b || dep.starts_with(b.as_str()))
445 })
446 })
447 .unwrap_or(false)
448 })
449 .cloned()
450 .collect();
451 if !params_table_keys.is_empty() {
452 parent_cache.invalidate_params_tables_for_item(idx, ¶ms_table_keys);
453
454 let eval_data_snapshot = self.eval_data.snapshot_data();
455 for key in ¶ms_table_keys {
456 let depends_on_subform_item = if let Some(deps) = self.dependencies.get(key) {
458 let subform_dep_prefix = format!("#/{}/properties/", root_key);
459 let subform_dep_prefix_short = format!("#/{}/", root_key);
460 deps.iter().any(|dep| {
461 dep.starts_with(&subform_dep_prefix)
462 || dep.starts_with(&subform_dep_prefix_short)
463 })
464 } else {
465 false
466 };
467
468 if depends_on_subform_item {
469 continue;
470 }
471
472 if let Ok((arc_val, external_deps_opt)) =
474 crate::jsoneval::table_evaluate::evaluate_table(
475 self,
476 key,
477 &EvalData::from_arc(std::sync::Arc::clone(&eval_data_snapshot)),
478 None,
479 )
480 {
481 if crate::utils::is_debug_cache_enabled() {
482 let rows_len = arc_val.as_array().map(|a| a.len()).unwrap_or(0);
483 println!("PARENT EVALUATED TABLE {} -> {} rows", key, rows_len);
484 }
485
486 if let Some(external_deps) = external_deps_opt {
487 parent_cache.active_item_index = None;
489 parent_cache.store_cache_arc(key, &external_deps, arc_val);
490 parent_cache.active_item_index = Some(idx);
491 }
492 } else {
493 if crate::utils::is_debug_cache_enabled() {
494 println!("PARENT EVALUATED TABLE {} -> ERROR", key);
495 }
496 }
497 }
498 }
499 }
500
501 {
503 let subform = self.subforms.get_mut(base_path).unwrap();
504 subform.static_arrays = std::sync::Arc::clone(&self.static_arrays);
505 subform
506 .engine
507 .set_static_arrays(std::sync::Arc::clone(&self.static_arrays));
508 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
509 }
510
511 let result = {
513 let subform = self.subforms.get_mut(base_path).unwrap();
514 f(subform)
515 };
516
517 {
519 let subform = self.subforms.get_mut(base_path).unwrap();
520 std::mem::swap(&mut subform.eval_cache, &mut parent_cache);
521 }
522 parent_cache.active_item_index = None;
523 self.eval_cache = parent_cache;
524
525 {
527 let subform = self.subforms.get_mut(base_path).unwrap();
528 if let Some(item_cache) = self.eval_cache.subform_caches.get_mut(&idx) {
529 if persist_evaluated_schema {
530 item_cache.evaluated_schema = Some(subform.evaluated_schema.clone());
531 }
532 subform.eval_cache.ensure_active_item_cache(idx);
533 if let Some(sub_cache) = subform.eval_cache.subform_caches.get_mut(&idx) {
534 sub_cache.item_snapshot = item_cache.item_snapshot.clone();
535 }
536 }
537 if !persist_evaluated_schema {
538 subform.eval_data = crate::jsoneval::eval_data::EvalData::new(Value::Null);
539 }
540 }
541
542 result
543 }
544
545 pub fn evaluate_subform(
557 &mut self,
558 subform_path: &str,
559 data: &str,
560 context: Option<&str>,
561 paths: Option<&[String]>,
562 token: Option<&CancellationToken>,
563 ) -> Result<(), String> {
564 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
565 if let Some(idx) = idx_opt {
566 self.evaluate_subform_item(&base_path, idx, data, context, paths, token)
567 } else {
568 let subform = self
569 .subforms
570 .get_mut(base_path.as_ref() as &str)
571 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
572 subform.evaluate(data, context, paths, token)
573 }
574 }
575
576 fn evaluate_subform_item(
578 &mut self,
579 base_path: &str,
580 idx: usize,
581 data: &str,
582 context: Option<&str>,
583 paths: Option<&[String]>,
584 token: Option<&CancellationToken>,
585 ) -> Result<(), String> {
586 let data_value = crate::jsoneval::json_parser::parse_json_str(data)
587 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
588 let context_value = if let Some(ctx) = context {
589 crate::jsoneval::json_parser::parse_json_str(ctx)
590 .map_err(|e| format!("Failed to parse subform context: {}", e))?
591 } else {
592 Value::Object(serde_json::Map::new())
593 };
594
595 self.with_item_cache_swap(base_path, idx, data_value, context_value, true, |sf| {
596 sf.evaluate_internal_pre_diffed(paths, token)?;
599 if sf.apply_visible_static_defaults_with_dependents(token)? {
600 sf.evaluate_internal_pre_diffed(paths, token)?;
601 }
602 Ok(())
603 })
604 }
605
606 pub fn validate_subform(
612 &mut self,
613 subform_path: &str,
614 data: &str,
615 context: Option<&str>,
616 paths: Option<&[String]>,
617 token: Option<&CancellationToken>,
618 validate_readonly: Option<bool>,
619 ) -> Result<crate::ValidationResult, String> {
620 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
621 if let Some(idx) = idx_opt {
622 let data_value = crate::jsoneval::json_parser::parse_json_str(data)
623 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
624 let context_value = if let Some(ctx) = context {
625 crate::jsoneval::json_parser::parse_json_str(ctx)
626 .map_err(|e| format!("Failed to parse subform context: {}", e))?
627 } else {
628 Value::Object(serde_json::Map::new())
629 };
630 let data_for_validation = data_value.clone();
631 self.with_item_cache_swap(
632 base_path.as_ref(),
633 idx,
634 data_value,
635 context_value,
636 false,
637 move |sf| {
638 sf.evaluate_internal_pre_diffed(paths, token)?;
640 sf.validate_pre_set(
641 Some(&data_for_validation),
642 paths,
643 token,
644 validate_readonly,
645 false,
646 )
647 },
648 )
649 } else {
650 let subform = self
651 .subforms
652 .get_mut(base_path.as_ref() as &str)
653 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
654 subform.validate(data, context, paths, token, validate_readonly, None)
655 }
656 }
657
658 pub fn evaluate_dependents_subform(
664 &mut self,
665 subform_path: &str,
666 changed_paths: &[String],
667 data: Option<&str>,
668 context: Option<&str>,
669 re_evaluate: bool,
670 token: Option<&CancellationToken>,
671 canceled_paths: Option<&mut Vec<String>>,
672 include_subforms: bool,
673 ) -> Result<Value, String> {
674 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
675 if let Some(idx) = idx_opt {
676 let (data_value, context_value) = if let Some(data_str) = data {
678 let dv = crate::jsoneval::json_parser::parse_json_str(data_str)
679 .map_err(|e| format!("Failed to parse subform data: {}", e))?;
680 let cv = if let Some(ctx) = context {
681 crate::jsoneval::json_parser::parse_json_str(ctx)
682 .map_err(|e| format!("Failed to parse subform context: {}", e))?
683 } else {
684 Value::Object(serde_json::Map::new())
685 };
686 (dv, cv)
687 } else {
688 let subform = self
690 .subforms
691 .get(base_path.as_ref() as &str)
692 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
693 let dv = if !subform.eval_data.data().is_null() {
694 subform.eval_data.snapshot_data_clone()
695 } else {
696 self.eval_data.snapshot_data_clone()
697 };
698 let cv = if !subform.eval_data.data().is_null() {
699 subform
700 .eval_data
701 .data()
702 .get("$context")
703 .cloned()
704 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
705 } else {
706 self.eval_data
707 .data()
708 .get("$context")
709 .cloned()
710 .unwrap_or_else(|| Value::Object(serde_json::Map::new()))
711 };
712 (dv, cv)
713 };
714 let changes = self.with_item_cache_swap(
715 base_path.as_ref(),
716 idx,
717 data_value,
718 context_value,
719 false,
720 |sf| {
721 sf.evaluate_dependents(
723 changed_paths,
724 None,
725 None,
726 re_evaluate,
727 token,
728 None,
729 include_subforms,
730 )
731 },
732 )?;
733 let subform_dot_path =
737 crate::jsoneval::path_utils::pointer_to_dot_notation(base_path.as_ref())
738 .replace(".properties.", ".");
739 let canonical_prefix = format!("{subform_dot_path}.{idx}");
740 let root_key = base_path.rsplit('/').next().unwrap_or(base_path.as_ref());
741 let changes = match changes {
742 Value::Array(changes) => Value::Array(
743 changes
744 .into_iter()
745 .map(|change| {
746 let Some(change_map) = change.as_object() else {
747 return change;
748 };
749 let Some(Value::String(reference)) = change_map.get("$ref") else {
750 return change;
751 };
752 let Some(suffix) = reference.strip_prefix(&canonical_prefix) else {
753 return change;
754 };
755 if !suffix.is_empty() && !suffix.starts_with('.') {
756 return change;
757 }
758 let mut mapped = change_map.clone();
759 mapped.insert(
760 "$ref".to_string(),
761 Value::String(format!("{root_key}{suffix}")),
762 );
763 Value::Object(mapped)
764 })
765 .collect(),
766 ),
767 value => value,
768 };
769 Ok(changes)
770 } else {
771 let subform = self
772 .subforms
773 .get_mut(base_path.as_ref() as &str)
774 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
775 subform.evaluate_dependents(
776 changed_paths,
777 data,
778 context,
779 re_evaluate,
780 token,
781 canceled_paths,
782 include_subforms,
783 )
784 }
785 }
786
787 pub fn resolve_layout_subform(
789 &mut self,
790 subform_path: &str,
791 evaluate: bool,
792 ) -> Result<ResolvedLayoutResult, String> {
793 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
794 let subform = self
795 .subforms
796 .get_mut(base_path.as_ref() as &str)
797 .ok_or_else(|| format!("Subform not found: {}", base_path))?;
798 subform.resolve_layout(evaluate)
799 }
800
801 pub fn get_evaluated_schema_subform(&mut self, subform_path: &str) -> Value {
803 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
804
805 if let Some(idx) = idx_opt {
806 if let Some(schema) = self
807 .eval_cache
808 .subform_caches
809 .get(&idx)
810 .and_then(|c| c.evaluated_schema.clone())
811 {
812 return schema;
813 }
814 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
815 subform.get_evaluated_schema()
816 } else {
817 Value::Null
818 }
819 } else if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
820 subform.get_evaluated_schema()
821 } else {
822 Value::Null
823 }
824 }
825
826 pub fn get_schema_value_subform(&mut self, subform_path: &str) -> Value {
832 let (base_path, idx_opt) = self.resolve_subform_path_alias(subform_path);
833 if let Some(idx) = idx_opt {
834 let data_value = self.eval_data.snapshot_data_clone();
835 let context_value = self
836 .eval_data
837 .data()
838 .get("$context")
839 .cloned()
840 .unwrap_or_else(|| Value::Object(serde_json::Map::new()));
841
842 let res = self.with_item_cache_swap(
843 &base_path,
844 idx,
845 data_value,
846 context_value,
847 false,
848 |sf| {
849 sf.evaluate_internal_pre_diffed(None, None)?;
850 if sf.apply_visible_static_defaults_with_dependents(None)? {
851 sf.evaluate_internal_pre_diffed(None, None)?;
852 }
853 Ok(sf.get_schema_value(Some(true)))
854 },
855 );
856
857 if let Ok(values) = res {
858 let schema_root_keys: Vec<String> = self
859 .subforms
860 .get(base_path.as_ref() as &str)
861 .and_then(|sf| sf.schema.as_object())
862 .into_iter()
863 .flat_map(|schema| schema.keys())
864 .filter(|key| !key.starts_with('$'))
865 .map(|k| k.to_string())
866 .collect();
867
868 return Value::Object(
869 schema_root_keys
870 .into_iter()
871 .filter_map(|key| values.get(&key).cloned().map(|value| (key, value)))
872 .collect(),
873 );
874 }
875 }
876
877 let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) else {
878 return Value::Null;
879 };
880
881 let values = subform.get_schema_value(None);
882 let Some(values) = values.as_object() else {
883 return values;
884 };
885
886 let schema_root_keys: Vec<&str> = subform
887 .schema
888 .as_object()
889 .into_iter()
890 .flat_map(|schema| schema.keys())
891 .filter(|key| !key.starts_with('$'))
892 .map(String::as_str)
893 .collect();
894
895 Value::Object(
896 schema_root_keys
897 .into_iter()
898 .filter_map(|key| {
899 values
900 .get(key)
901 .cloned()
902 .map(|value| (key.to_string(), value))
903 })
904 .collect(),
905 )
906 }
907
908 pub fn get_schema_value_array_subform(&self, subform_path: &str) -> Value {
910 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
911 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
912 subform.get_schema_value_array()
913 } else {
914 Value::Array(vec![])
915 }
916 }
917
918 pub fn get_schema_value_object_subform(&self, subform_path: &str) -> Value {
920 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
921 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
922 subform.get_schema_value_object()
923 } else {
924 Value::Object(serde_json::Map::new())
925 }
926 }
927
928 pub fn get_evaluated_schema_without_params_subform(&mut self, subform_path: &str) -> Value {
930 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
931 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
932 subform.get_evaluated_schema_without_params()
933 } else {
934 Value::Null
935 }
936 }
937
938 pub fn get_plain_params_subform(&self, subform_path: &str) -> Option<Value> {
940 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
941 let subform = self.subforms.get(base_path.as_ref() as &str)?;
942 subform.get_plain_params()
943 }
944
945 pub fn get_evaluated_params_subform(
947 &mut self,
948 subform_path: &str,
949 with_static_array: bool,
950 ) -> Option<Value> {
951 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
952 let subform = self.subforms.get_mut(base_path.as_ref() as &str)?;
953 subform.get_evaluated_params(with_static_array)
954 }
955
956 pub fn get_evaluated_schema_by_path_subform(
958 &mut self,
959 subform_path: &str,
960 schema_path: &str,
961 ) -> Option<Value> {
962 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
963 self.subforms.get_mut(base_path.as_ref() as &str).map(|sf| {
964 sf.get_evaluated_schema_by_paths(&[schema_path.to_string()], Some(ReturnFormat::Nested))
965 })
966 }
967
968 pub fn get_evaluated_schema_by_paths_subform(
970 &mut self,
971 subform_path: &str,
972 schema_paths: &[String],
973 format: Option<crate::ReturnFormat>,
974 ) -> Value {
975 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
976 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
977 subform.get_evaluated_schema_by_paths(
978 schema_paths,
979 Some(format.unwrap_or(ReturnFormat::Flat)),
980 )
981 } else {
982 match format.unwrap_or_default() {
983 crate::ReturnFormat::Array => Value::Array(vec![]),
984 _ => Value::Object(serde_json::Map::new()),
985 }
986 }
987 }
988
989 pub fn get_schema_by_path_subform(
991 &self,
992 subform_path: &str,
993 schema_path: &str,
994 ) -> Option<Value> {
995 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
996 self.subforms
997 .get(base_path.as_ref() as &str)
998 .and_then(|sf| sf.get_schema_by_path(schema_path))
999 }
1000
1001 pub fn get_schema_by_paths_subform(
1003 &self,
1004 subform_path: &str,
1005 schema_paths: &[String],
1006 format: Option<crate::ReturnFormat>,
1007 ) -> Value {
1008 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
1009 if let Some(subform) = self.subforms.get(base_path.as_ref() as &str) {
1010 subform.get_schema_by_paths(schema_paths, Some(format.unwrap_or(ReturnFormat::Flat)))
1011 } else {
1012 match format.unwrap_or_default() {
1013 crate::ReturnFormat::Array => Value::Array(vec![]),
1014 _ => Value::Object(serde_json::Map::new()),
1015 }
1016 }
1017 }
1018
1019 pub fn get_resolved_layout_subform(&mut self, subform_path: &str) -> ResolvedLayoutResult {
1021 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
1022 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
1023 subform.get_resolved_layout()
1024 } else {
1025 ResolvedLayoutResult::default()
1026 }
1027 }
1028
1029 pub fn get_evaluated_schema_resolved_subform(&mut self, subform_path: &str) -> Value {
1031 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
1032 if let Some(subform) = self.subforms.get_mut(base_path.as_ref() as &str) {
1033 subform.get_evaluated_schema_resolved()
1034 } else {
1035 Value::Null
1036 }
1037 }
1038
1039 pub fn get_subform_paths(&self) -> Vec<String> {
1041 self.subforms.keys().cloned().collect()
1042 }
1043
1044 pub fn has_subform(&self, subform_path: &str) -> bool {
1046 let (base_path, _) = self.resolve_subform_path_alias(subform_path);
1047 self.subforms.contains_key(base_path.as_ref() as &str)
1048 }
1049}