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