rust_store_core/
datasource.rs1use serde_json::Value;
18
19use crate::dialect::Backend;
20
21pub const DEFAULT_SOURCE: &str = "default";
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum DataSource {
27 Mongo,
29 Sql(Backend),
31}
32
33impl DataSource {
34 pub fn from_kind(kind: &str) -> Result<DataSource, String> {
36 match kind.to_lowercase().as_str() {
37 "mongo" | "mongodb" => Ok(DataSource::Mongo),
38 other => Backend::parse(other).map(DataSource::Sql),
39 }
40 }
41
42 pub fn as_str(&self) -> &'static str {
44 match self {
45 DataSource::Mongo => "mongo",
46 DataSource::Sql(b) => b.as_str(),
47 }
48 }
49
50 pub fn is_mongo(&self) -> bool {
51 matches!(self, DataSource::Mongo)
52 }
53
54 pub fn backend(&self) -> Option<Backend> {
56 match self {
57 DataSource::Sql(b) => Some(*b),
58 DataSource::Mongo => None,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Default)]
65pub struct DataSourceConfig {
66 sources: Vec<(String, DataSource)>,
68}
69
70impl DataSourceConfig {
71 pub fn new() -> Self {
72 Self::default()
73 }
74
75 pub fn from_json(v: &Value) -> Result<Self, String> {
79 let mut cfg = Self::new();
80 let Some(obj) = v.as_object() else {
81 return Ok(cfg);
82 };
83 if let Some(Value::Object(sources)) = obj.get("sources") {
84 for (name, kind) in sources {
85 let kind = kind
86 .as_str()
87 .ok_or_else(|| format!("数据源 {} 的 kind 必须是字符串", name))?;
88 cfg.register(name, DataSource::from_kind(kind)?);
89 }
90 }
91 Ok(cfg)
92 }
93
94 pub fn register(&mut self, name: &str, source: DataSource) {
96 if let Some(slot) = self.sources.iter_mut().find(|(n, _)| n == name) {
97 slot.1 = source;
98 } else {
99 self.sources.push((name.to_string(), source));
100 }
101 }
102
103 pub fn get(&self, name: &str) -> Option<DataSource> {
104 self.sources
105 .iter()
106 .find(|(n, _)| n == name)
107 .map(|(_, s)| *s)
108 }
109
110 pub fn resolve(&self, source: Option<&str>) -> Result<DataSource, String> {
115 match source {
116 None => Ok(self.get(DEFAULT_SOURCE).unwrap_or(DataSource::Mongo)),
117 Some(name) => self
118 .get(name)
119 .ok_or_else(|| format!("schema 绑定的数据源未在配置中声明: {}", name)),
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use serde_json::json;
128
129 #[test]
130 fn parse_and_resolve() {
131 let cfg = DataSourceConfig::from_json(&json!({
132 "sources": { "default": "mongo", "mysql1": "mysql", "pg1": "postgres", "lite": "sqlite" }
133 }))
134 .unwrap();
135
136 assert_eq!(cfg.resolve(None).unwrap(), DataSource::Mongo);
137 assert_eq!(
138 cfg.resolve(Some("mysql1")).unwrap(),
139 DataSource::Sql(Backend::Mysql)
140 );
141 assert_eq!(
142 cfg.resolve(Some("pg1")).unwrap(),
143 DataSource::Sql(Backend::Postgres)
144 );
145 assert_eq!(
146 cfg.resolve(Some("lite")).unwrap(),
147 DataSource::Sql(Backend::Sqlite)
148 );
149 }
150
151 #[test]
152 fn empty_config_defaults_to_mongo() {
153 let cfg = DataSourceConfig::from_json(&Value::Null).unwrap();
154 assert_eq!(cfg.resolve(None).unwrap(), DataSource::Mongo);
155 assert!(cfg.resolve(Some("missing")).is_err());
157 }
158
159 #[test]
160 fn unknown_kind_and_source_error() {
161 assert!(DataSourceConfig::from_json(&json!({ "sources": { "x": "oracle" } })).is_err());
162 let cfg =
163 DataSourceConfig::from_json(&json!({ "sources": { "default": "mongo" } })).unwrap();
164 assert!(cfg.resolve(Some("nope")).is_err());
165 }
166
167 #[test]
168 fn kind_roundtrip() {
169 assert_eq!(DataSource::from_kind("mongodb").unwrap().as_str(), "mongo");
170 assert_eq!(DataSource::from_kind("MySQL").unwrap().as_str(), "mysql");
171 assert_eq!(
172 DataSource::from_kind("pg").unwrap().backend(),
173 Some(Backend::Postgres)
174 );
175 assert!(DataSource::Mongo.is_mongo());
176 }
177}