rust_store_core/schema/
definition.rs1use 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 pub fields: Option<Value>,
18 pub strategy: Option<String>,
22}
23
24#[derive(Debug, Clone)]
25pub struct RelationDef {
26 pub model: String,
27 pub rel_type: String,
28 pub local_field: String,
29 pub foreign_field: String,
30 pub read: Option<Vec<String>>,
31}
32
33#[derive(Debug, Clone)]
34pub struct ComputeDef {
35 pub comp_type: String,
36 pub has_fn: bool,
37 pub has_async_fn: bool,
38 pub fn_ref: Option<String>,
40 pub agg: Option<Value>,
43 pub depends: Vec<String>,
44 pub read: Option<Vec<String>>,
45}
46
47#[derive(Debug, Clone)]
48pub struct Schema {
49 pub name: String,
50 pub collection: String,
51 pub id_prefix: String,
52 pub timestamps: bool,
53 pub fields: HashMap<String, FieldDef>,
54 pub relations: HashMap<String, RelationDef>,
55 pub computes: Vec<(String, ComputeDef)>,
57 pub read: Option<Vec<String>>,
58 pub write: Option<Vec<String>>,
59 pub indexes: Vec<Value>,
61 pub datasource: Option<String>,
63 pub namespace: Option<String>,
67}
68
69impl Schema {
70 pub fn compute(&self, name: &str) -> Option<&ComputeDef> {
71 self.computes
72 .iter()
73 .find(|(k, _)| k == name)
74 .map(|(_, c)| c)
75 }
76
77 pub fn source(&self) -> &str {
79 self.datasource
80 .as_deref()
81 .unwrap_or(crate::datasource::DEFAULT_SOURCE)
82 }
83
84 pub fn ns(&self) -> Option<&str> {
86 self.namespace.as_deref().filter(|s| !s.is_empty())
87 }
88
89 pub fn id_is_autoincrement(&self) -> bool {
91 self.fields.get("_id").and_then(|f| f.strategy.as_deref()) == Some("autoincrement")
92 }
93}
94
95pub(super) fn normalize_fields(v: Option<&Value>) -> Result<HashMap<String, FieldDef>, String> {
97 let mut out = HashMap::new();
98 let Some(Value::Object(map)) = v else {
99 return Ok(out);
100 };
101 for (key, val) in map {
102 let fd = if let Some(s) = val.as_str() {
103 FieldDef {
104 field_type: s.to_string(),
105 required: false,
106 default: None,
107 read: None,
108 write: None,
109 fields: None,
110 strategy: None,
111 }
112 } else if let Some(o) = val.as_object() {
113 FieldDef {
114 field_type: o
115 .get("type")
116 .and_then(|v| v.as_str())
117 .unwrap_or("")
118 .to_string(),
119 required: o.get("required").and_then(|v| v.as_bool()).unwrap_or(false),
120 default: o.get("default").filter(|v| !v.is_null()).cloned(),
121 read: str_list(o.get("read")),
122 write: str_list(o.get("write")),
123 fields: o.get("fields").filter(|v| is_truthy(v)).cloned(),
124 strategy: match o.get("strategy").and_then(|v| v.as_str()) {
125 None => None,
126 Some(v @ "autoincrement") => Some(v.to_string()),
127 Some(other) => {
129 return Err(format!(
130 "字段 {key} 的 strategy \"{other}\" 非法(仅支持 \"autoincrement\",且仅限 _id 字段)"
131 ))
132 }
133 },
134 }
135 } else {
136 return Err(format!("字段 {} 定义类型非法(须为类型字符串或对象)", key));
140 };
141 out.insert(key.clone(), fd);
142 }
143 Ok(out)
144}
145
146pub fn map_of(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
148 let mut m = Map::new();
149 for (k, v) in pairs {
150 m.insert(k.to_string(), v);
151 }
152 m
153}