1use json::{object, JsonValue};
2use serde::{Deserialize, Serialize};
3use std::collections::BTreeMap;
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Clone, Debug, Deserialize, Serialize)]
8pub struct Config {
9 pub default: String,
10 pub connections: BTreeMap<String, Connection>,
11}
12
13impl Default for Config {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl Config {
20 pub fn new() -> Config {
21 let mut connections = BTreeMap::new();
22 connections.insert("sqlite".to_string(), Connection::new("sqlite"));
23 connections.insert("mysql".to_string(), Connection::new("mysql"));
24 connections.insert("pgsql".to_string(), Connection::new("pgsql"));
25 connections.insert("mssql".to_string(), Connection::new("mssql"));
26 Self {
27 default: "sqlite".to_string(),
28 connections,
29 }
30 }
31 pub fn from(data: JsonValue) -> Config {
32 let default = data["default"].to_string();
33 let mut connections = BTreeMap::new();
34 for (key, value) in data["connections"].entries() {
35 let connection = Connection::from(value.clone()).clone();
36 connections.insert(key.to_string(), connection.clone());
37 }
38
39 Self {
40 default,
41 connections,
42 }
43 }
44 pub fn create(config_file: PathBuf, pkg_name: bool) -> Config {
45 #[derive(Clone, Debug, Deserialize, Serialize)]
46 pub struct ConfigPkg {
47 pub br_db: Config,
48 }
49 impl ConfigPkg {
50 pub fn new() -> ConfigPkg {
51 let mut connections = BTreeMap::new();
52 connections.insert("sqlite".to_string(), Connection::new("sqlite"));
53 connections.insert("mysql".to_string(), Connection::new("mysql"));
54 connections.insert("pgsql".to_string(), Connection::new("pgsql"));
55 connections.insert("mssql".to_string(), Connection::new("mssql"));
56 Self {
57 br_db: Config {
58 default: "sqlite".to_string(),
59 connections,
60 },
61 }
62 }
63 }
64 match fs::read_to_string(config_file.clone()) {
65 Ok(e) => {
66 if pkg_name {
67 toml::from_str::<ConfigPkg>(&e)
68 .map(|c| c.br_db)
69 .unwrap_or_else(|_| Config::new())
70 } else {
71 toml::from_str::<Config>(&e).unwrap_or_else(|_| Config::new())
72 }
73 }
74 Err(_) => {
75 if pkg_name {
76 let data = ConfigPkg::new();
77 if let Some(parent) = config_file.parent() {
78 let _ = fs::create_dir_all(parent);
79 }
80 if let Ok(toml) = toml::to_string(&data) {
81 if let Some(path) = config_file.to_str() {
82 let _ = fs::write(path, toml);
83 }
84 }
85 data.br_db
86 } else {
87 let data = Config::new();
88 if let Some(parent) = config_file.parent() {
89 let _ = fs::create_dir_all(parent);
90 }
91 if let Ok(toml) = toml::to_string(&data) {
92 if let Some(path) = config_file.to_str() {
93 let _ = fs::write(path, toml);
94 }
95 }
96 data
97 }
98 }
99 }
100 }
101 pub fn set_connection(&mut self, name: &str, connection: JsonValue) {
103 let connection = Connection::from(connection);
104 self.connections.insert(name.to_string(), connection);
105 }
106 pub fn set_default(&mut self, name: &str) {
108 self.default = name.to_string();
109 }
110}
111#[derive(Clone, Debug, Deserialize, Serialize)]
113pub enum Mode {
114 Mysql,
115 Mssql,
116 Sqlite,
117 Pgsql,
118 None,
119}
120
121impl Mode {
122 pub fn str(&mut self) -> String {
123 match self {
124 Mode::Mysql => "mysql",
125 Mode::Sqlite => "sqlite",
126 Mode::Mssql => "mssql",
127 Mode::Pgsql => "pgsql",
128 Mode::None => "",
129 }
130 .to_string()
131 }
132 pub fn from(name: &str) -> Self {
133 match name.to_lowercase().as_str() {
134 "mysql" => Mode::Mysql,
135 "sqlite" => Mode::Sqlite,
136 "mssql" => Mode::Mssql,
137 "pgsql" => Mode::Pgsql,
138 _ => Mode::None,
139 }
140 }
141}
142
143#[derive(Clone, Debug, Deserialize, Serialize)]
145pub struct PoolConfig {
146 pub min_connections: u32,
147 pub max_connections: u32,
148 pub connect_timeout_secs: u64,
149 pub read_timeout_secs: u64,
150 pub write_timeout_secs: u64,
151 pub keepalive_ms: u64,
152}
153
154impl Default for PoolConfig {
155 fn default() -> Self {
156 Self {
157 min_connections: 0,
158 max_connections: 400,
159 connect_timeout_secs: 5,
160 read_timeout_secs: 15,
161 write_timeout_secs: 20,
162 keepalive_ms: 5000,
163 }
164 }
165}
166
167impl PoolConfig {
168 pub fn from(data: &JsonValue) -> Self {
169 Self {
170 min_connections: data["min_connections"].as_u32().unwrap_or(0),
171 max_connections: data["max_connections"].as_u32().unwrap_or(400),
172 connect_timeout_secs: data["connect_timeout_secs"].as_u64().unwrap_or(5),
173 read_timeout_secs: data["read_timeout_secs"].as_u64().unwrap_or(15),
174 write_timeout_secs: data["write_timeout_secs"].as_u64().unwrap_or(20),
175 keepalive_ms: data["keepalive_ms"].as_u64().unwrap_or(5000),
176 }
177 }
178}
179
180#[derive(Clone, Debug, Deserialize, Serialize)]
182pub struct Connection {
183 pub mode: Mode,
184 pub hostname: String,
185 pub hostport: String,
186 pub database: String,
187 pub username: String,
188 pub userpass: String,
189 pub params: Vec<String>,
190 pub charset: Charset,
191 pub prefix: String,
192 pub debug: bool,
193 #[serde(default)]
194 pub pool: PoolConfig,
195}
196
197impl Default for Connection {
198 fn default() -> Self {
199 Self::new("sqlite")
200 }
201}
202
203impl Connection {
204 pub fn new(mode: &str) -> Connection {
205 let mut that = Self {
206 mode: Mode::from(mode),
207 hostname: "".to_string(),
208 hostport: "".to_string(),
209 database: "".to_string(),
210 username: "".to_string(),
211 userpass: "".to_string(),
212 params: vec![],
213 charset: Charset::Utf8mb4,
214 prefix: "".to_string(),
215 debug: false,
216 pool: PoolConfig::default(),
217 };
218 match Mode::from(mode) {
219 Mode::Mysql => {
220 that.hostname = "127.0.0.1".to_string();
221 that.hostport = "3306".to_string();
222 that.database = "test".to_string();
223 that.username = "test".to_string();
224 that.userpass = "test".to_string();
225 }
226 Mode::Mssql => {}
227 Mode::Sqlite => {
228 that.database = "db/app.db".to_string();
229 }
230 Mode::Pgsql => {
231 that.hostname = "127.0.0.1".to_string();
232 that.hostport = "5432".to_string();
233 that.username = "test".to_string();
234 that.userpass = "test".to_string();
235 }
236 Mode::None => {}
237 }
238
239 that
240 }
241 pub fn json(&mut self) -> JsonValue {
242 object! {
243 mode: self.mode.str(),
244 hostname: self.hostname.clone(),
245 hostport: self.hostport.clone(),
246 database: self.database.clone(),
247 username: self.username.clone(),
248 userpass:self.userpass.clone(),
249 params: self.params.clone(),
250 charset: self.charset.str(),
251 prefix: self.prefix.clone(),
252 debug: self.debug
253 }
254 }
255 pub fn from(data: JsonValue) -> Connection {
256 Self {
257 mode: Mode::from(data["mode"].as_str().unwrap_or("none")),
258 hostname: data["hostname"].to_string(),
259 hostport: data["hostport"].to_string(),
260 database: data["database"].to_string(),
261 username: data["username"].to_string(),
262 userpass: data["userpass"].to_string(),
263 params: data["params"].members().map(|x| x.to_string()).collect(),
264 charset: Charset::from(data["charset"].as_str().unwrap_or("utf8mb4")),
265 prefix: data["prefix"].as_str().unwrap_or("").to_string(),
266 debug: data["debug"].to_string().parse::<bool>().unwrap_or(false),
267 pool: PoolConfig::from(&data["pool"]),
268 }
269 }
270 pub fn get_dsn(self) -> String {
271 match self.mode {
272 Mode::Mysql => {
273 format!(
274 "mysql://{}:{}@{}:{}/{}",
275 self.username, self.userpass, self.hostname, self.hostport, self.database
276 )
277 }
278 Mode::Sqlite => {
279 let db_path = self.database.as_str();
280 let path_buf = PathBuf::from(db_path);
281 if !path_buf.is_file() {
282 if let Some(file_name) = path_buf.file_name() {
283 if let Some(file_name_str) = file_name.to_str() {
284 let dir_path = db_path.trim_end_matches(file_name_str);
285 let _ = fs::create_dir_all(dir_path);
286 }
287 }
288 }
289 path_buf.to_str().unwrap_or(db_path).to_string()
290 }
291 Mode::Mssql => format!(
292 "sqlsrv://{}:{}@{}:{}/{}",
293 self.username, self.userpass, self.hostname, self.hostport, self.database
294 ),
295 Mode::Pgsql => format!(
296 "host={} user={} password={} dbname={}",
297 self.hostname, self.username, self.userpass, self.database
298 ),
299 Mode::None => "".to_string(),
300 }
301 }
302}
303
304#[derive(Clone, Debug, Deserialize, Serialize)]
305pub enum Charset {
306 Utf8mb4,
307 Utf8,
308 None,
309}
310
311impl Charset {
312 pub fn from(str: &str) -> Charset {
313 match str.to_lowercase().as_str() {
314 "utf8" => Charset::Utf8,
315 "utf8mb4" => Charset::Utf8mb4,
316 _ => Charset::None,
317 }
318 }
319 pub fn str(&self) -> String {
320 match self {
321 Charset::Utf8 => "utf8",
322 Charset::Utf8mb4 => "utf8mb4",
323 Charset::None => "",
324 }
325 .to_string()
326 }
327}