rust_store_core/computes/
inject.rs1use std::collections::HashMap;
4
5use serde_json::{Map, Value};
6
7use crate::pipeline::{parse, tokenize, RelAst};
8use crate::schema::Schema;
9
10use super::cache::ensure_cache;
11
12#[derive(Debug, Clone, PartialEq)]
14pub struct RelDep {
15 pub name: String,
16 pub fields: Vec<String>,
18}
19
20impl RelDep {
21 pub fn to_value(&self) -> Value {
22 Value::Object(Map::from_iter([
23 ("name".to_string(), Value::String(self.name.clone())),
24 (
25 "fields".to_string(),
26 Value::Array(self.fields.iter().cloned().map(Value::String).collect()),
27 ),
28 ]))
29 }
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub enum Injected {
35 All,
36 Fields(Vec<String>),
37}
38
39#[derive(Debug, Clone, PartialEq, Default)]
40pub struct InjectInfo {
41 pub relations: Vec<(String, Injected)>,
42 pub fields: Vec<String>,
45}
46
47impl InjectInfo {
48 pub fn is_empty(&self) -> bool {
49 self.relations.is_empty() && self.fields.is_empty()
50 }
51
52 pub fn to_value(&self) -> Value {
54 let mut m = Map::new();
55 for (name, inj) in &self.relations {
56 let v = match inj {
57 Injected::All => Value::String("__all__".to_string()),
58 Injected::Fields(f) => Value::Array(f.iter().cloned().map(Value::String).collect()),
59 };
60 m.insert(name.clone(), v);
61 }
62 let mut root = Map::new();
63 if let Some(rm) = Value::Object(m).as_object() {
64 if !rm.is_empty() {
65 root.insert("relations".to_string(), Value::Object(rm.clone()));
66 }
67 }
68 if !self.fields.is_empty() {
69 root.insert(
70 "fields".to_string(),
71 Value::Array(self.fields.iter().cloned().map(Value::String).collect()),
72 );
73 }
74 Value::Object(Map::from_iter([(
75 "inject".to_string(),
76 Value::Object(root),
77 )]))
78 }
79
80 pub fn from_value(v: &Value) -> InjectInfo {
82 let mut relations = Vec::new();
83 let mut fields = Vec::new();
84 if let Some(Value::Object(m)) = v.get("inject").or(Some(v)) {
85 if let Some(Value::Object(rm)) = m.get("relations") {
86 for (name, inj) in rm {
87 let parsed = match inj {
88 Value::String(s) if s == "__all__" => Injected::All,
89 Value::Array(arr) => Injected::Fields(
90 arr.iter()
91 .map(|f| f.as_str().unwrap_or_default().to_string())
92 .collect(),
93 ),
94 _ => continue,
95 };
96 relations.push((name.clone(), parsed));
97 }
98 }
99 if let Some(Value::Array(fa)) = m.get("fields") {
100 fields = fa
101 .iter()
102 .filter_map(|f| f.as_str().map(String::from))
103 .collect();
104 }
105 }
106 InjectInfo { relations, fields }
107 }
108}
109
110fn dep_slot<'a>(deps: &'a mut Vec<RelDep>, name: &str) -> &'a mut RelDep {
111 let idx = match deps.iter().position(|d| d.name == name) {
112 Some(i) => i,
113 None => {
114 deps.push(RelDep {
115 name: name.to_string(),
116 fields: Vec::new(),
117 });
118 deps.len() - 1
119 }
120 };
121 &mut deps[idx]
122}
123
124pub fn collect_field_deps(fields: &mut Vec<String>, schema: &Schema) -> Vec<String> {
129 let cache = ensure_cache(schema);
130 let mut added = Vec::new();
131 for entry in &cache.async_fn_list {
132 for dep in &entry.depends {
133 let trimmed = dep.trim();
134 if trimmed.is_empty() || trimmed == "_id" {
135 continue;
136 }
137 if trimmed.contains('{') || schema.relations.contains_key(trimmed) {
139 continue;
140 }
141 if !fields.contains(&trimmed.to_string()) {
142 fields.push(trimmed.to_string());
143 added.push(trimmed.to_string());
144 }
145 }
146 }
147 added
148}
149
150pub fn collect_rel_deps(schema: &Schema) -> Result<Vec<RelDep>, String> {
152 let cache = ensure_cache(schema);
153 let mut deps: Vec<RelDep> = Vec::new();
154
155 for entry in &cache.async_fn_list {
156 for dep in &entry.depends {
157 let trimmed = dep.trim();
158 if trimmed.is_empty() || trimmed == "_id" {
159 continue;
160 }
161 if trimmed.contains('{') {
162 let parsed = parse(&tokenize(trimmed))?;
163 if !schema.relations.contains_key(&parsed.model) {
164 continue;
165 }
166 let slot = dep_slot(&mut deps, &parsed.model);
167 for f in &parsed.fields {
168 if f != "_id" && !slot.fields.contains(f) {
169 slot.fields.push(f.clone());
170 }
171 }
172 } else {
173 if !schema.relations.contains_key(trimmed) {
174 continue;
175 }
176 dep_slot(&mut deps, trimmed);
177 }
178 }
179 }
180
181 Ok(deps)
182}
183
184pub fn inject_into_ast(relations: &mut Vec<(String, RelAst)>, rel_deps: &[RelDep]) -> InjectInfo {
186 let mut info = InjectInfo::default();
187
188 for dep in rel_deps {
189 match relations.iter().position(|(n, _)| n == &dep.name) {
190 Some(idx) => {
191 let (_, rel_ast) = &mut relations[idx];
192 let mut added = Vec::new();
193 for f in &dep.fields {
194 if !rel_ast.fields.contains(f) {
195 rel_ast.fields.push(f.clone());
196 added.push(f.clone());
197 }
198 }
199 if !added.is_empty() {
200 info.relations
201 .push((dep.name.clone(), Injected::Fields(added)));
202 }
203 }
204 None => {
205 relations.push((
206 dep.name.clone(),
207 RelAst {
208 fields: dep.fields.clone(),
209 relations: Vec::new(),
210 params: HashMap::new(),
211 },
212 ));
213 info.relations.push((dep.name.clone(), Injected::All));
214 }
215 }
216 }
217
218 info
219}
220
221pub fn merge_depends_into_ast(
223 relations: &mut Vec<(String, RelAst)>,
224 schema: &Schema,
225) -> Result<InjectInfo, String> {
226 if ensure_cache(schema).async_fn_list.is_empty() {
227 return Ok(InjectInfo::default());
228 }
229 let rel_deps = collect_rel_deps(schema)?;
230 if rel_deps.is_empty() {
231 return Ok(InjectInfo::default());
232 }
233 Ok(inject_into_ast(relations, &rel_deps))
234}
235
236pub fn strip_dep_injected(items: &mut [Value], info: &InjectInfo) {
238 if info.is_empty() {
239 return;
240 }
241 for item in items.iter_mut() {
242 let Some(o) = item.as_object_mut() else {
243 continue;
244 };
245 if !info.fields.is_empty() {
247 for f in &info.fields {
248 o.remove(f);
249 }
250 }
251 for (rel_name, injected) in &info.relations {
252 match injected {
253 Injected::All => {
254 o.remove(rel_name);
255 }
256 Injected::Fields(fields) => match o.get_mut(rel_name) {
257 Some(Value::Array(arr)) => {
258 for sub in arr.iter_mut() {
259 if let Some(so) = sub.as_object_mut() {
260 for f in fields {
261 so.remove(f);
262 }
263 }
264 }
265 }
266 Some(Value::Object(so)) => {
267 for f in fields {
268 so.remove(f);
269 }
270 }
271 _ => {}
272 },
273 }
274 }
275 }
276}