Skip to main content

rust_store_core/schema/
definition.rs

1//! Schema 的数据结构与字段规范化(`Registry` 见 [`super::registry`])。
2
3use std::collections::HashMap;
4
5use serde_json::{Map, Value};
6
7use crate::types::{is_truthy, str_list};
8
9#[derive(Debug, Clone)]
10pub struct FieldDef {
11    pub field_type: String,
12    pub required: bool,
13    pub default: Option<Value>,
14    pub read: Option<Vec<String>>,
15    pub write: Option<Vec<String>>,
16    /// 嵌套 object 字段的原始定义(未规范化,与 JS 保持一致)
17    pub fields: Option<Value>,
18}
19
20#[derive(Debug, Clone)]
21pub struct RelationDef {
22    pub model: String,
23    pub rel_type: String,
24    pub local_field: String,
25    pub foreign_field: String,
26    pub read: Option<Vec<String>>,
27}
28
29#[derive(Debug, Clone)]
30pub struct ComputeDef {
31    pub comp_type: String,
32    pub has_fn: bool,
33    pub has_async_fn: bool,
34    /// 回调标识:Host 经 FnRegistry 把它绑定到实现;缺省 = 计算列 key 名
35    pub fn_ref: Option<String>,
36    /// 归一聚合算子(§9.2(2)):`{"$count": "lessons"}` / `{"$sum": "lessons.duration"}`。
37    /// 取代旧的 `lookup` + `addFields` Mongo 专用形态;SQL / Mongo 走同一语法(执行下推优先)。
38    pub agg: Option<Value>,
39    pub depends: Vec<String>,
40    pub read: Option<Vec<String>>,
41}
42
43#[derive(Debug, Clone)]
44pub struct Schema {
45    pub name: String,
46    pub collection: String,
47    pub id_prefix: String,
48    pub timestamps: bool,
49    pub fields: HashMap<String, FieldDef>,
50    pub relations: HashMap<String, RelationDef>,
51    /// 保持注册顺序:`build_agg_stages` 的输出顺序依赖它
52    pub computes: Vec<(String, ComputeDef)>,
53    pub read: Option<Vec<String>>,
54    pub write: Option<Vec<String>>,
55    /// 原始索引定义(`[{keys: {...}, options: {...}}]`),upsert 条件构建依赖 unique 索引
56    pub indexes: Vec<Value>,
57    /// 绑定的数据源名(可选;缺省 = `default`,回落单源 Mongo)。见 [`crate::datasource`]
58    pub datasource: Option<String>,
59    /// 连接内的库/schema 名(可选;缺省 = `null`,用连接自身默认:Mongo db 实例的库名、
60    /// PG 的 search_path、MySQL 的连接库、SQLite 的 main)。
61    /// 与 `datasource`/`collection` 构成定位三元组,见 [`super::Registry::get_by_location`]。
62    pub namespace: Option<String>,
63}
64
65impl Schema {
66    pub fn compute(&self, name: &str) -> Option<&ComputeDef> {
67        self.computes
68            .iter()
69            .find(|(k, _)| k == name)
70            .map(|(_, c)| c)
71    }
72
73    /// 解析后的数据源名(缺省 `default`)
74    pub fn source(&self) -> &str {
75        self.datasource
76            .as_deref()
77            .unwrap_or(crate::datasource::DEFAULT_SOURCE)
78    }
79
80    /// 解析后的 namespace(空串归一为 `None`)
81    pub fn ns(&self) -> Option<&str> {
82        self.namespace.as_deref().filter(|s| !s.is_empty())
83    }
84}
85
86/// 规范化 fields 定义(字符串简写 → `{type, required:false}`)
87pub(super) fn normalize_fields(v: Option<&Value>) -> Result<HashMap<String, FieldDef>, String> {
88    let mut out = HashMap::new();
89    let Some(Value::Object(map)) = v else {
90        return Ok(out);
91    };
92    for (key, val) in map {
93        let fd = if let Some(s) = val.as_str() {
94            FieldDef {
95                field_type: s.to_string(),
96                required: false,
97                default: None,
98                read: None,
99                write: None,
100                fields: None,
101            }
102        } else if let Some(o) = val.as_object() {
103            FieldDef {
104                field_type: o
105                    .get("type")
106                    .and_then(|v| v.as_str())
107                    .unwrap_or("")
108                    .to_string(),
109                required: o.get("required").and_then(|v| v.as_bool()).unwrap_or(false),
110                default: o.get("default").filter(|v| !v.is_null()).cloned(),
111                read: str_list(o.get("read")),
112                write: str_list(o.get("write")),
113                fields: o.get("fields").filter(|v| is_truthy(v)).cloned(),
114            }
115        } else {
116            // 非字符串、非对象的定义(如 `fields: { price: 123 }`)= 脏 schema,
117            // fail-fast 报错而非静默产出空 field_type(后续 object/array 展平、
118            // 标量列判定都会走错且无信号)
119            return Err(format!("字段 {} 定义类型非法(须为类型字符串或对象)", key));
120        };
121        out.insert(key.clone(), fd);
122    }
123    Ok(out)
124}
125
126/// 便捷构造:`Map` from `[(&str, Value)]`
127pub fn map_of(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
128    let mut m = Map::new();
129    for (k, v) in pairs {
130        m.insert(k.to_string(), v);
131    }
132    m
133}