Skip to main content

rust_store_core/command/
finalize.rs

1//! 结果回喂:后处理 / asyncFn 桥 / 剥离注入
2
3use serde_json::Value;
4
5use crate::computes::{
6    process_node, run_async_fns, select_async_fns, strip_dep_injected, FnRegistry, InjectInfo,
7};
8use crate::permission::Context;
9use crate::pipeline::Ast;
10use crate::schema::Registry;
11
12/// 从 `postprocess` 还原查询 AST(`{ast, inject}` 形状)
13fn postprocess_ast(postprocess: &Value) -> Result<Ast, String> {
14    let ast_v = postprocess
15        .get("ast")
16        .ok_or_else(|| "postprocess 缺少 ast".to_string())?;
17    Ast::from_value(ast_v)
18}
19
20/// finalize 第一阶段:逐条 `process_node`(补默认值 / 同步 fn / 递归下钻 / 权限裁剪),
21/// 并返回**待 Host 执行的 asyncFn 回调标识**(`fnRef`,已做 read 权限过滤)。
22///
23/// 与 [`finalize_query`] 的差异:asyncFn 是宿主语言原生闭包,无法跨 FFI 执行,
24/// 因此不在 core 内调用,而是把 `fnRef` 列表交给 Host,由其依次
25/// `await jsFn(items, ctx)` 后再调 [`strip_query`]。
26pub fn prepare_query(
27    postprocess: &Value,
28    items: &mut [Value],
29    registry: &Registry,
30    fn_registry: Option<&dyn FnRegistry>,
31    ctx: Option<&Context>,
32) -> Result<Vec<String>, String> {
33    if postprocess.is_null() {
34        return Ok(Vec::new());
35    }
36    let mut ast = postprocess_ast(postprocess)?;
37    let schema = registry.get(&ast.model)?;
38
39    // ① 逐条 process_node(同步 fn 计算列内联执行)
40    for item in items.iter_mut() {
41        process_node(
42            item,
43            &mut ast.fields,
44            &mut ast.relations,
45            schema,
46            ctx,
47            registry,
48            fn_registry,
49        )?;
50    }
51
52    // 返回 Host 需异步执行的回调标识
53    Ok(select_async_fns(registry.role_rules(), schema, ctx)
54        .into_iter()
55        .map(|e| e.fn_ref)
56        .collect())
57}
58
59/// finalize 第三阶段:剥离 asyncFn 依赖注入的字段
60pub fn strip_query(postprocess: &Value, items: &mut [Value]) {
61    if postprocess.is_null() {
62        return;
63    }
64    if let Some(inj_v) = postprocess.get("inject") {
65        if !inj_v.is_null() {
66            let info = InjectInfo::from_value(inj_v);
67            strip_dep_injected(items, &info);
68        }
69    }
70}
71
72/// 读路径尾部后处理(对应 JS `_postprocess` 三段时序)
73///
74/// Host 执行完命令序列拿到原始文档后回喂 core:
75///   1. 逐条 `process_node`(补默认值 → 同步 fn → 递归下钻 → 权限裁剪)
76///   2. 批量执行 asyncFn 计算列(权限过滤)
77///   3. 剥离 asyncFn 依赖注入的字段
78///
79/// `postprocess` 即 [`QueryPlan::to_value`] 里的 `postprocess` 字段(`{ast, inject}` 形状);
80/// 传入 `null` 视为无后处理(绑定层防御性兜底)。
81///
82/// 这是「core 内直接执行 asyncFn」的入口(Rust Host / 测试用);
83/// 跨 FFI 的绑定应改用 [`prepare_query`] + [`strip_query`] 两段式。
84pub fn finalize_query(
85    postprocess: &Value,
86    items: &mut [Value],
87    registry: &Registry,
88    fn_registry: Option<&dyn FnRegistry>,
89    ctx: Option<&Context>,
90) -> Result<(), String> {
91    if postprocess.is_null() {
92        return Ok(());
93    }
94
95    // ① 逐条 process_node
96    prepare_query(postprocess, items, registry, fn_registry, ctx)?;
97
98    // ② 批量 asyncFn(权限过滤)
99    let ast = postprocess_ast(postprocess)?;
100    let schema = registry.get(&ast.model)?;
101    run_async_fns(items, registry.role_rules(), schema, ctx, fn_registry)?;
102
103    // ③ 剥离依赖注入的字段
104    strip_query(postprocess, items);
105    Ok(())
106}