json_eval_rs/parse_schema/
common.rs1use crate::jsoneval::path_utils;
2use crate::jsoneval::table_metadata::ColumnMetadata;
3use indexmap::IndexSet;
5use serde_json::Value;
6
7pub fn collect_refs(value: &Value, refs: &mut IndexSet<String>) {
9 match value {
10 Value::Object(map) => {
11 if let Some(path) = map.get("$ref").and_then(Value::as_str) {
12 refs.insert(path_utils::normalize_to_json_pointer(path).into_owned());
13 }
14 if let Some(path) = map.get("ref").and_then(Value::as_str) {
15 refs.insert(path_utils::normalize_to_json_pointer(path).into_owned());
16 }
17 if let Some(var_val) = map.get("var") {
18 match var_val {
19 Value::String(s) => {
20 refs.insert(s.clone());
21 }
22 Value::Array(arr) => {
23 if let Some(path) = arr.get(0).and_then(Value::as_str) {
24 refs.insert(path.to_string());
25 }
26 }
27 _ => {}
28 }
29 }
30 for val in map.values() {
31 collect_refs(val, refs);
32 }
33 }
34 Value::Array(arr) => {
35 for val in arr {
36 collect_refs(val, refs);
37 }
38 }
39 _ => {}
40 }
41}
42
43#[inline]
46pub fn has_actionable_keys(value: &Value) -> bool {
47 match value {
48 Value::Object(map) => {
49 if map.contains_key("$evaluation")
50 || map.contains_key("$table")
51 || map.contains_key("dependents")
52 || map.contains_key("$layout")
53 {
54 return true;
55 }
56
57 if let Some(Value::Object(condition)) = map.get("condition") {
59 if condition.contains_key("hidden") || condition.contains_key("disabled") {
60 return true;
61 }
62 }
63
64 if map.contains_key("rules") {
66 return true;
67 }
68
69 if let Some(Value::String(type_str)) = map.get("type") {
71 if type_str == "array" && map.contains_key("items") {
72 return true;
73 }
74 }
75
76 if let Some(Value::String(url)) = map.get("url") {
78 if url.contains('{') && url.contains('}') {
79 return true;
80 }
81 }
82
83 map.values().any(has_actionable_keys)
84 }
85 Value::Array(arr) => arr.iter().take(5).any(has_actionable_keys),
86 _ => false,
87 }
88}
89
90pub fn compute_column_partitions(columns: &[ColumnMetadata]) -> (Vec<usize>, Vec<usize>) {
95 use std::collections::HashSet;
96
97 let mut fwd_cols = HashSet::new();
99 for col in columns {
100 if col.has_forward_ref {
101 fwd_cols.insert(col.name.as_ref());
102 }
103 }
104
105 loop {
107 let mut changed = false;
108 for col in columns {
109 if !fwd_cols.contains(col.name.as_ref()) {
110 for dep in col.dependencies.iter() {
112 let dep_name = dep.trim_start_matches('$');
114 if fwd_cols.contains(dep_name) {
115 fwd_cols.insert(col.name.as_ref());
116 changed = true;
117 break;
118 }
119 }
120 }
121 }
122 if !changed {
124 break;
125 }
126 }
127
128 let mut forward_indices = Vec::new();
130 let mut normal_indices = Vec::new();
131
132 for (idx, col) in columns.iter().enumerate() {
133 if fwd_cols.contains(col.name.as_ref()) {
134 forward_indices.push(idx);
135 } else {
136 normal_indices.push(idx);
137 }
138 }
139
140 (forward_indices, normal_indices)
141}