use std::collections::HashMap;
use serde_json::{Map, Value};
use crate::types::{is_truthy, str_list};
#[derive(Debug, Clone)]
pub struct FieldDef {
pub field_type: String,
pub required: bool,
pub default: Option<Value>,
pub read: Option<Vec<String>>,
pub write: Option<Vec<String>>,
pub fields: Option<Value>,
}
#[derive(Debug, Clone)]
pub struct RelationDef {
pub model: String,
pub rel_type: String,
pub local_field: String,
pub foreign_field: String,
pub read: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct ComputeDef {
pub comp_type: String,
pub has_fn: bool,
pub has_async_fn: bool,
pub fn_ref: Option<String>,
pub agg: Option<Value>,
pub depends: Vec<String>,
pub read: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct Schema {
pub name: String,
pub collection: String,
pub id_prefix: String,
pub timestamps: bool,
pub fields: HashMap<String, FieldDef>,
pub relations: HashMap<String, RelationDef>,
pub computes: Vec<(String, ComputeDef)>,
pub read: Option<Vec<String>>,
pub write: Option<Vec<String>>,
pub indexes: Vec<Value>,
pub datasource: Option<String>,
pub namespace: Option<String>,
}
impl Schema {
pub fn compute(&self, name: &str) -> Option<&ComputeDef> {
self.computes
.iter()
.find(|(k, _)| k == name)
.map(|(_, c)| c)
}
pub fn source(&self) -> &str {
self.datasource
.as_deref()
.unwrap_or(crate::datasource::DEFAULT_SOURCE)
}
pub fn ns(&self) -> Option<&str> {
self.namespace.as_deref().filter(|s| !s.is_empty())
}
}
pub(super) fn normalize_fields(v: Option<&Value>) -> Result<HashMap<String, FieldDef>, String> {
let mut out = HashMap::new();
let Some(Value::Object(map)) = v else {
return Ok(out);
};
for (key, val) in map {
let fd = if let Some(s) = val.as_str() {
FieldDef {
field_type: s.to_string(),
required: false,
default: None,
read: None,
write: None,
fields: None,
}
} else if let Some(o) = val.as_object() {
FieldDef {
field_type: o
.get("type")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string(),
required: o.get("required").and_then(|v| v.as_bool()).unwrap_or(false),
default: o.get("default").filter(|v| !v.is_null()).cloned(),
read: str_list(o.get("read")),
write: str_list(o.get("write")),
fields: o.get("fields").filter(|v| is_truthy(v)).cloned(),
}
} else {
return Err(format!("字段 {} 定义类型非法(须为类型字符串或对象)", key));
};
out.insert(key.clone(), fd);
}
Ok(out)
}
pub fn map_of(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
let mut m = Map::new();
for (k, v) in pairs {
m.insert(k.to_string(), v);
}
m
}