use url::Url;
use super::GatekeepSqlxBackend;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum SqlxDriver {
Postgres,
Sqlite,
MySql,
}
impl SqlxDriver {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Postgres => "postgres",
Self::Sqlite => "sqlite",
Self::MySql => "mysql",
}
}
#[must_use]
pub const fn is_enabled(self) -> bool {
match self {
Self::Postgres => cfg!(feature = "postgres"),
Self::Sqlite => cfg!(feature = "sqlite"),
Self::MySql => cfg!(feature = "mysql"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum SqlxDriverError {
#[error("unsupported SQLx database URL scheme {scheme:?}")]
UnsupportedUrlScheme {
scheme: Option<String>,
},
#[error("SQLx driver {driver} is not enabled for gatekeep-sqlx")]
DriverNotEnabled {
driver: &'static str,
},
#[error("SQLx backend mismatch: expected {expected}, found {actual}")]
BackendMismatch {
expected: &'static str,
actual: &'static str,
},
}
pub fn infer_enabled_driver_from_url(database_url: &str) -> Result<SqlxDriver, SqlxDriverError> {
let driver = infer_driver_from_url(database_url)?;
if driver.is_enabled() {
Ok(driver)
} else {
Err(SqlxDriverError::DriverNotEnabled {
driver: driver.name(),
})
}
}
pub fn validate_database_url_for_backend<B>(database_url: &str) -> Result<(), SqlxDriverError>
where
B: GatekeepSqlxBackend,
{
let actual = infer_enabled_driver_from_url(database_url)?;
if actual == B::DRIVER {
Ok(())
} else {
Err(SqlxDriverError::BackendMismatch {
expected: B::NAME,
actual: actual.name(),
})
}
}
fn infer_driver_from_url(database_url: &str) -> Result<SqlxDriver, SqlxDriverError> {
if database_url.starts_with("sqlite:") {
return Ok(SqlxDriver::Sqlite);
}
let Some((scheme, rest)) = database_url.split_once(':') else {
return Err(SqlxDriverError::UnsupportedUrlScheme { scheme: None });
};
match scheme {
"postgres" | "postgresql" => Ok(SqlxDriver::Postgres),
"mysql" | "mariadb" => Ok(SqlxDriver::MySql),
"sqlite" => Ok(SqlxDriver::Sqlite),
_ => Err(SqlxDriverError::UnsupportedUrlScheme {
scheme: Url::parse(database_url)
.ok()
.filter(|url| rest.starts_with("//") && url.scheme().eq_ignore_ascii_case(scheme))
.map(|_| scheme.to_owned()),
}),
}
}