use std::error::Error;
use std::fmt::Debug;
use std::io;
use std::path::{Path, PathBuf};
pub mod common;
pub mod drivers;
pub mod macros;
pub mod migrate;
#[doc = include_str!("reference.toml")]
pub mod _reference {}
#[cfg(all(test, feature = "sqlx-toml"))]
mod tests;
#[derive(Debug, Default)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case", deny_unknown_fields)
)]
pub struct Config {
pub common: common::Config,
pub drivers: drivers::Config,
pub macros: macros::Config,
pub migrate: migrate::Config,
}
#[derive(thiserror::Error, Debug)]
pub enum ConfigError {
#[error("environment variable `CARGO_MANIFEST_DIR` must be set and valid")]
Env(
#[from]
#[source]
std::env::VarError,
),
#[error("config file {path:?} not found")]
NotFound { path: PathBuf },
#[error("error reading config file {path:?}")]
Io {
path: PathBuf,
#[source]
error: io::Error,
},
#[error("error parsing config file {path:?}")]
Parse {
path: PathBuf,
#[source]
error: Box<dyn Error + Send + Sync + 'static>,
},
#[error("SQLx found config file at {path:?} but the `sqlx-toml` feature was not enabled")]
ParseDisabled { path: PathBuf },
}
impl ConfigError {
pub fn from_io(path: impl Into<PathBuf>, error: io::Error) -> Self {
if error.kind() == io::ErrorKind::NotFound {
Self::NotFound { path: path.into() }
} else {
Self::Io {
path: path.into(),
error,
}
}
}
pub fn not_found_path(&self) -> Option<&Path> {
if let Self::NotFound { path } = self {
Some(path)
} else {
None
}
}
}
#[allow(clippy::result_large_err)]
impl Config {
pub fn try_from_crate_or_default() -> Result<Self, ConfigError> {
Self::try_from_path_or_default(get_crate_path()?)
}
pub fn try_from_path_or_default(path: PathBuf) -> Result<Self, ConfigError> {
Self::read_from(path).or_else(|e| {
if let ConfigError::NotFound { .. } = e {
Ok(Config::default())
} else {
Err(e)
}
})
}
pub fn try_from_path(path: PathBuf) -> Result<Self, ConfigError> {
Self::read_from(path)
}
#[cfg(feature = "sqlx-toml")]
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
let toml_s = match std::fs::read_to_string(&path) {
Ok(toml) => toml,
Err(error) => {
return Err(ConfigError::from_io(path, error));
}
};
tracing::debug!("read config TOML from {path:?}:\n{toml_s}");
toml::from_str(&toml_s).map_err(|error| ConfigError::Parse {
path,
error: Box::new(error),
})
}
#[cfg(not(feature = "sqlx-toml"))]
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
match path.try_exists() {
Ok(true) => Err(ConfigError::ParseDisabled { path }),
Ok(false) => Err(ConfigError::NotFound { path }),
Err(e) => Err(ConfigError::from_io(path, e)),
}
}
}
fn get_crate_path() -> Result<PathBuf, ConfigError> {
let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
path.push("sqlx.toml");
Ok(path)
}