rws_server/model/backend.rs
1//! [`Backend`] — which compiled-in database driver a [`super::DbPool`] uses.
2
3use super::DbError;
4
5/// Selects which compiled-in database backend a [`super::DbPool`] talks to.
6///
7/// Unlike most of this crate's feature flags, `model-sqlite`, `model-postgres`,
8/// and `model-mysql` are not mutually exclusive: enabling more than one in the
9/// same binary compiles a `DbPool` variant for each, so e.g. a hot-path SQLite
10/// pool and an analytics-tier Postgres pool can coexist in one process. Each
11/// `DbPool`/`DbConfig` picks one backend to talk to via this enum.
12///
13/// `Backend` only has variants for the `model-*` features actually compiled
14/// in — matching on it is exhaustive without a wildcard arm, so enabling a
15/// backend and forgetting a match arm for it is a compile error, not a silent
16/// gap.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Backend {
19 #[cfg(feature = "model-sqlite")]
20 Sqlite,
21 #[cfg(feature = "model-postgres")]
22 Postgres,
23 #[cfg(feature = "model-mysql")]
24 MySql,
25}
26
27impl Backend {
28 /// Parse a backend name as read from `RWS_DB_BACKEND`: `"sqlite"`,
29 /// `"postgres"`/`"postgresql"`, or `"mysql"` (case-insensitive). Errors
30 /// (naming the backends actually compiled in) if `s` names a backend that
31 /// either doesn't exist or wasn't compiled in.
32 pub fn parse(s: &str) -> Result<Backend, DbError> {
33 match s.to_ascii_lowercase().as_str() {
34 #[cfg(feature = "model-sqlite")]
35 "sqlite" => Ok(Backend::Sqlite),
36 #[cfg(feature = "model-postgres")]
37 "postgres" | "postgresql" => Ok(Backend::Postgres),
38 #[cfg(feature = "model-mysql")]
39 "mysql" => Ok(Backend::MySql),
40 other => Err(DbError::new(format!(
41 "unknown or not-compiled-in database backend '{}' (compiled in: {})",
42 other,
43 Backend::compiled_in_names(),
44 ))),
45 }
46 }
47
48 /// Lowercase name, e.g. for error messages and logging.
49 pub fn name(&self) -> &'static str {
50 match self {
51 #[cfg(feature = "model-sqlite")]
52 Backend::Sqlite => "sqlite",
53 #[cfg(feature = "model-postgres")]
54 Backend::Postgres => "postgres",
55 #[cfg(feature = "model-mysql")]
56 Backend::MySql => "mysql",
57 }
58 }
59
60 fn compiled_in_names() -> String {
61 let mut names: Vec<&str> = Vec::new();
62 #[cfg(feature = "model-sqlite")]
63 names.push("sqlite");
64 #[cfg(feature = "model-postgres")]
65 names.push("postgres");
66 #[cfg(feature = "model-mysql")]
67 names.push("mysql");
68 names.join(", ")
69 }
70
71 /// `Some(backend)` if exactly one `model-*` feature is compiled in — the
72 /// unambiguous default used by [`super::DbConfig::from_env`] when
73 /// `RWS_DB_BACKEND` isn't set. `None` when more than one is compiled in,
74 /// which requires the caller to say explicitly which one they mean.
75 #[doc(hidden)]
76 pub fn unambiguous_default() -> Option<Backend> {
77 let candidates: &[Backend] = &[
78 #[cfg(feature = "model-sqlite")]
79 Backend::Sqlite,
80 #[cfg(feature = "model-postgres")]
81 Backend::Postgres,
82 #[cfg(feature = "model-mysql")]
83 Backend::MySql,
84 ];
85 if candidates.len() == 1 { Some(candidates[0]) } else { None }
86 }
87}