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 Local,
33}
34
35impl DataSource {
36 pub fn from_kind(kind: &str) -> Result<DataSource, String> {
38 match kind.to_lowercase().as_str() {
39 "mongo" | "mongodb" => Ok(DataSource::Mongo),
40 "local" => Ok(DataSource::Local),
41 other => Backend::parse(other).map(DataSource::Sql),
42 }
43 }
44
45 pub fn as_str(&self) -> &'static str {
47 match self {
48 DataSource::Mongo => "mongo",
49 DataSource::Sql(b) => b.as_str(),
50 DataSource::Local => "local",
51 }
52 }
53
54 pub fn is_mongo(&self) -> bool {
55 matches!(self, DataSource::Mongo)
56 }
57
58 pub fn is_local(&self) -> bool {
59 matches!(self, DataSource::Local)
60 }
61
62 pub fn backend(&self) -> Option<Backend> {
64 match self {
65 DataSource::Sql(b) => Some(*b),
66 DataSource::Mongo | DataSource::Local => None,
67 }
68 }
69}
70
71#[derive(Debug, Clone, Default)]
73pub struct DataSourceConfig {
74 sources: Vec<(String, DataSource)>,
76}
77
78impl DataSourceConfig {
79 pub fn new() -> Self {
80 Self::default()
81 }
82
83 pub fn from_json(v: &Value) -> Result<Self, String> {
87 let mut cfg = Self::new();
88 let Some(obj) = v.as_object() else {
89 return Ok(cfg);
90 };
91 if let Some(Value::Object(sources)) = obj.get("sources") {
92 for (name, kind) in sources {
93 let kind = kind
94 .as_str()
95 .ok_or_else(|| format!("数据源 {} 的 kind 必须是字符串", name))?;
96 cfg.register(name, DataSource::from_kind(kind)?);
97 }
98 }
99 Ok(cfg)
100 }
101
102 pub fn register(&mut self, name: &str, source: DataSource) {
104 if let Some(slot) = self.sources.iter_mut().find(|(n, _)| n == name) {
105 slot.1 = source;
106 } else {
107 self.sources.push((name.to_string(), source));
108 }
109 }
110
111 pub fn get(&self, name: &str) -> Option<DataSource> {
112 self.sources
113 .iter()
114 .find(|(n, _)| n == name)
115 .map(|(_, s)| *s)
116 }
117
118 pub fn resolve(&self, source: Option<&str>) -> Result<DataSource, String> {
123 match source {
124 None => Ok(self.get(DEFAULT_SOURCE).unwrap_or(DataSource::Mongo)),
125 Some(name) => self
126 .get(name)
127 .ok_or_else(|| format!("schema 绑定的数据源未在配置中声明: {}", name)),
128 }
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use serde_json::json;
136
137 #[test]
138 fn parse_and_resolve() {
139 let cfg = DataSourceConfig::from_json(&json!({
140 "sources": { "default": "mongo", "mysql1": "mysql", "pg1": "postgres", "lite": "sqlite" }
141 }))
142 .unwrap();
143
144 assert_eq!(cfg.resolve(None).unwrap(), DataSource::Mongo);
145 assert_eq!(
146 cfg.resolve(Some("mysql1")).unwrap(),
147 DataSource::Sql(Backend::Mysql)
148 );
149 assert_eq!(
150 cfg.resolve(Some("pg1")).unwrap(),
151 DataSource::Sql(Backend::Postgres)
152 );
153 assert_eq!(
154 cfg.resolve(Some("lite")).unwrap(),
155 DataSource::Sql(Backend::Sqlite)
156 );
157 }
158
159 #[test]
160 fn empty_config_defaults_to_mongo() {
161 let cfg = DataSourceConfig::from_json(&Value::Null).unwrap();
162 assert_eq!(cfg.resolve(None).unwrap(), DataSource::Mongo);
163 assert!(cfg.resolve(Some("missing")).is_err());
165 }
166
167 #[test]
168 fn unknown_kind_and_source_error() {
169 assert!(DataSourceConfig::from_json(&json!({ "sources": { "x": "oracle" } })).is_err());
170 let cfg =
171 DataSourceConfig::from_json(&json!({ "sources": { "default": "mongo" } })).unwrap();
172 assert!(cfg.resolve(Some("nope")).is_err());
173 }
174
175 #[test]
176 fn kind_roundtrip() {
177 assert_eq!(DataSource::from_kind("mongodb").unwrap().as_str(), "mongo");
178 assert_eq!(DataSource::from_kind("MySQL").unwrap().as_str(), "mysql");
179 assert_eq!(
180 DataSource::from_kind("pg").unwrap().backend(),
181 Some(Backend::Postgres)
182 );
183 assert!(DataSource::Mongo.is_mongo());
184 }
185
186 #[test]
187 fn local_kind_roundtrip() {
188 let ds = DataSource::from_kind("local").unwrap();
189 assert_eq!(ds, DataSource::Local);
190 assert_eq!(ds.as_str(), "local");
191 assert!(ds.backend().is_none());
192 assert!(ds.is_local());
193 assert!(!DataSource::Mongo.is_local());
194 assert!(DataSource::from_kind("oracle").is_err());
196 }
197}