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
92 .get("_id")
93 .and_then(|f| f.strategy.as_deref())
94 == Some("autoincrement")
95 }
96}
97
98pub(super) fn normalize_fields(v: Option<&Value>) -> Result<HashMap<String, FieldDef>, String> {
100 let mut out = HashMap::new();
101 let Some(Value::Object(map)) = v else {
102 return Ok(out);
103 };
104 for (key, val) in map {
105 let fd = if let Some(s) = val.as_str() {
106 FieldDef {
107 field_type: s.to_string(),
108 required: false,
109 default: None,
110 read: None,
111 write: None,
112 fields: None,
113 strategy: None,
114 }
115 } else if let Some(o) = val.as_object() {
116 FieldDef {
117 field_type: o
118 .get("type")
119 .and_then(|v| v.as_str())
120 .unwrap_or("")
121 .to_string(),
122 required: o.get("required").and_then(|v| v.as_bool()).unwrap_or(false),
123 default: o.get("default").filter(|v| !v.is_null()).cloned(),
124 read: str_list(o.get("read")),
125 write: str_list(o.get("write")),
126 fields: o.get("fields").filter(|v| is_truthy(v)).cloned(),
127 strategy: match o.get("strategy").and_then(|v| v.as_str()) {
128 None => None,
129 Some(v @ "autoincrement") => Some(v.to_string()),
130 Some(other) => {
132 return Err(format!(
133 "字段 {key} 的 strategy \"{other}\" 非法(仅支持 \"autoincrement\",且仅限 _id 字段)"
134 ))
135 }
136 },
137 }
138 } else {
139 return Err(format!("字段 {} 定义类型非法(须为类型字符串或对象)", key));
143 };
144 out.insert(key.clone(), fd);
145 }
146 Ok(out)
147}
148
149pub fn map_of(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
151 let mut m = Map::new();
152 for (k, v) in pairs {
153 m.insert(k.to_string(), v);
154 }
155 m
156}