refactor: make Config always compiled

simplifies usage while still making parsing optional for less generated code
This commit is contained in:
Austin Bonander 2024-09-18 01:54:22 -07:00
parent 9f34fc8dd2
commit e775d2a3eb
12 changed files with 117 additions and 65 deletions

View File

@ -54,16 +54,14 @@ features = ["all-databases", "_unstable-all-types", "_unstable-doc"]
rustdoc-args = ["--cfg", "docsrs"]
[features]
default = ["any", "macros", "migrate", "json", "config-all"]
default = ["any", "macros", "migrate", "json", "sqlx-toml"]
derive = ["sqlx-macros/derive"]
macros = ["derive", "sqlx-macros/macros"]
migrate = ["sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate"]
# Enable parsing of `sqlx.toml` for configuring macros, migrations, or both.
config-macros = ["sqlx-macros?/config-macros"]
config-migrate = ["sqlx-macros?/config-migrate"]
config-all = ["config-macros", "config-migrate"]
# Enable parsing of `sqlx.toml` for configuring macros and migrations.
sqlx-toml = ["sqlx-core/sqlx-toml", "sqlx-macros?/sqlx-toml"]
# intended mainly for CI and docs
all-databases = ["mysql", "sqlite", "postgres", "any"]
@ -79,7 +77,7 @@ _unstable-all-types = [
"bit-vec",
]
# Render documentation that wouldn't otherwise be shown (e.g. `sqlx_core::config`).
_unstable-doc = ["config-all", "sqlx-core/_unstable-doc"]
_unstable-doc = []
# Base runtime features without TLS
runtime-async-std = ["_rt-async-std", "sqlx-core/_rt-async-std", "sqlx-macros?/_rt-async-std"]

View File

@ -49,7 +49,8 @@ filetime = "0.2"
backoff = { version = "0.4.0", features = ["futures", "tokio"] }
[features]
default = ["postgres", "sqlite", "mysql", "native-tls", "completions"]
default = ["postgres", "sqlite", "mysql", "native-tls", "completions", "sqlx-toml"]
rustls = ["sqlx/runtime-tokio-rustls"]
native-tls = ["sqlx/runtime-tokio-native-tls"]
@ -64,6 +65,8 @@ openssl-vendored = ["openssl/vendored"]
completions = ["dep:clap_complete"]
sqlx-toml = ["sqlx/sqlx-toml"]
[dev-dependencies]
assert_cmd = "2.0.11"
tempfile = "3.10.1"

View File

@ -12,7 +12,7 @@ features = ["offline"]
[features]
default = []
migrate = ["sha2", "crc", "config-migrate"]
migrate = ["sha2", "crc"]
any = []
@ -31,11 +31,13 @@ _tls-none = []
# support offline/decoupled building (enables serialization of `Describe`)
offline = ["serde", "either/serde"]
config = ["serde", "toml/parse"]
config-macros = ["config"]
config-migrate = ["config"]
# Enable parsing of `sqlx.toml`.
# For simplicity, the `config` module is always enabled,
# but disabling this disables the `serde` derives and the `toml` crate,
# which is a good bit less code to compile if the feature isn't being used.
sqlx-toml = ["serde", "toml/parse"]
_unstable-doc = ["config-macros", "config-migrate"]
_unstable-doc = ["sqlx-toml"]
[dependencies]
# Runtimes

View File

@ -1,5 +1,6 @@
/// Configuration shared by multiple components.
#[derive(Debug, Default, serde::Deserialize)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
pub struct Config {
/// Override the database URL environment variable.
///
@ -36,3 +37,9 @@ pub struct Config {
/// and the ones used in `bar` will use `BAR_DATABASE_URL`.
pub database_url_var: Option<String>,
}
impl Config {
pub fn database_url_var(&self) -> &str {
self.database_url_var.as_deref().unwrap_or("DATABASE_URL")
}
}

View File

@ -1,8 +1,8 @@
use std::collections::BTreeMap;
/// Configuration for the `query!()` family of macros.
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
pub struct Config {
/// Specify the crate to use for mapping date/time types to Rust.
///
@ -235,8 +235,12 @@ pub struct Config {
}
/// The crate to use for mapping date/time types to Rust.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DateTimeCrate {
/// Use whichever crate is enabled (`time` then `chrono`).
#[default]

View File

@ -12,8 +12,8 @@ use std::collections::BTreeSet;
/// if the proper precautions are not taken.
///
/// Be sure you know what you are doing and that you read all relevant documentation _thoroughly_.
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
pub struct Config {
/// Override the name of the table used to track executed migrations.
///
@ -118,8 +118,12 @@ pub struct Config {
}
/// The default type of migration that `sqlx migrate create` should create by default.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DefaultMigrationType {
/// Create the same migration type as that of the latest existing migration,
/// or `Simple` otherwise.
@ -134,8 +138,12 @@ pub enum DefaultMigrationType {
}
/// The default scheme that `sqlx migrate create` should use for version integers.
#[derive(Debug, Default, PartialEq, Eq, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum DefaultVersioning {
/// Infer the versioning scheme from existing migrations:
///

View File

@ -7,6 +7,7 @@
//!
//! See the [reference][`_reference`] for the full `sqlx.toml` file.
use std::error::Error;
use std::fmt::Debug;
use std::io;
use std::path::{Path, PathBuf};
@ -23,13 +24,11 @@ pub mod common;
/// Configuration for the `query!()` family of macros.
///
/// See [`macros::Config`] for details.
#[cfg(feature = "config-macros")]
pub mod macros;
/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
///
/// See [`migrate::Config`] for details.
#[cfg(feature = "config-migrate")]
pub mod migrate;
/// Reference for `sqlx.toml` files
@ -41,11 +40,12 @@ pub mod migrate;
/// ```
pub mod _reference {}
#[cfg(test)]
#[cfg(all(test, feature = "sqlx-toml"))]
mod tests;
/// The parsed structure of a `sqlx.toml` file.
#[derive(Debug, Default, serde::Deserialize)]
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
pub struct Config {
/// Configuration shared by multiple components.
///
@ -55,21 +55,11 @@ pub struct Config {
/// Configuration for the `query!()` family of macros.
///
/// See [`macros::Config`] for details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "config-all", feature = "config-macros")))
)]
#[cfg(feature = "config-macros")]
pub macros: macros::Config,
/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
///
/// See [`migrate::Config`] for details.
#[cfg_attr(
docsrs,
doc(cfg(any(feature = "config-all", feature = "config-migrate")))
)]
#[cfg(feature = "config-migrate")]
pub migrate: migrate::Config,
}
@ -90,13 +80,17 @@ pub enum ConfigError {
std::env::VarError,
),
/// No configuration file was found. Not necessarily fatal.
#[error("config file {path:?} not found")]
NotFound {
path: PathBuf,
},
/// An I/O error occurred while attempting to read the config file at `path`.
///
/// This includes [`io::ErrorKind::NotFound`].
///
/// [`Self::not_found_path()`] will return the path if the file was not found.
/// If the error is [`io::ErrorKind::NotFound`], [`Self::NotFound`] is returned instead.
#[error("error reading config file {path:?}")]
Read {
Io {
path: PathBuf,
#[source]
error: io::Error,
@ -105,22 +99,41 @@ pub enum ConfigError {
/// An error in the TOML was encountered while parsing the config file at `path`.
///
/// The error gives line numbers and context when printed with `Display`/`ToString`.
///
/// Only returned if the `sqlx-toml` feature is enabled.
#[error("error parsing config file {path:?}")]
Parse {
path: PathBuf,
/// Type-erased [`toml::de::Error`].
#[source]
error: toml::de::Error,
error: Box<dyn Error + Send + Sync + 'static>,
},
/// A `sqlx.toml` file was found or specified, but the `sqlx-toml` feature is not enabled.
#[error("SQLx found config file at {path:?} but the `sqlx-toml` feature was not enabled")]
ParseDisabled {
path: PathBuf
},
}
impl ConfigError {
/// Create a [`ConfigError`] from a [`std::io::Error`].
///
/// Maps to either `NotFound` or `Io`.
pub fn from_io(path: PathBuf, error: io::Error) -> Self {
if error.kind() == io::ErrorKind::NotFound {
Self::NotFound { path }
} else {
Self::Io { path, error }
}
}
/// If this error means the file was not found, return the path that was attempted.
pub fn not_found_path(&self) -> Option<&Path> {
match self {
ConfigError::Read { path, error } if error.kind() == io::ErrorKind::NotFound => {
Some(path)
}
_ => None,
if let Self::NotFound { path } = self {
Some(path)
} else {
None
}
}
}
@ -140,14 +153,22 @@ impl Config {
/// If the file exists but an unrecoverable error was encountered while parsing it.
pub fn from_crate() -> &'static Self {
Self::try_from_crate().unwrap_or_else(|e| {
if let Some(path) = e.not_found_path() {
// Non-fatal
tracing::debug!("Not reading config, file {path:?} not found (error: {e})");
CACHE.get_or_init(Config::default)
} else {
match e {
ConfigError::NotFound { path } => {
// Non-fatal
tracing::debug!("Not reading config, file {path:?} not found");
CACHE.get_or_init(Config::default)
}
// FATAL ERRORS BELOW:
// In the case of migrations,
// we can't proceed with defaults as they may be completely wrong.
panic!("failed to read sqlx config: {e}")
e @ ConfigError::ParseDisabled { .. } => {
// Only returned if the file exists but the feature is not enabled.
panic!("{e}")
}
e => {
panic!("failed to read sqlx config: {e}")
}
}
})
}
@ -188,12 +209,13 @@ impl Config {
})
}
#[cfg(feature = "sqlx-toml")]
fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
// The `toml` crate doesn't provide an incremental reader.
let toml_s = match std::fs::read_to_string(&path) {
Ok(toml) => toml,
Err(error) => {
return Err(ConfigError::Read { path, error });
return Err(ConfigError::from_io(path, error));
}
};
@ -201,6 +223,15 @@ impl Config {
// Motivation: https://github.com/toml-rs/toml/issues/761
tracing::debug!("read config TOML from {path:?}:\n{toml_s}");
toml::from_str(&toml_s).map_err(|error| ConfigError::Parse { path, error })
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))
}
}
}

View File

@ -20,7 +20,6 @@ fn assert_common_config(config: &config::common::Config) {
assert_eq!(config.database_url_var.as_deref(), Some("FOO_DATABASE_URL"));
}
#[cfg(feature = "config-macros")]
fn assert_macros_config(config: &config::macros::Config) {
use config::macros::*;
@ -74,7 +73,6 @@ fn assert_macros_config(config: &config::macros::Config) {
);
}
#[cfg(feature = "config-migrate")]
fn assert_migrate_config(config: &config::migrate::Config) {
use config::migrate::*;

View File

@ -91,7 +91,6 @@ pub mod any;
#[cfg(feature = "migrate")]
pub mod testing;
#[cfg(feature = "config")]
pub mod config;
pub use error::{Error, Result};

View File

@ -26,9 +26,7 @@ derive = []
macros = []
migrate = ["sqlx-core/migrate"]
config = ["sqlx-core/config"]
config-macros = ["config", "sqlx-core/config-macros"]
config-migrate = ["config", "sqlx-core/config-migrate"]
sqlx-toml = ["sqlx-core/sqlx-toml"]
# database
mysql = ["sqlx-mysql"]

View File

@ -16,6 +16,7 @@ use crate::query::data::{hash_string, DynQueryData, QueryData};
use crate::query::input::RecordType;
use either::Either;
use url::Url;
use sqlx_core::config::Config;
mod args;
mod data;
@ -138,8 +139,12 @@ static METADATA: Lazy<Metadata> = Lazy::new(|| {
let offline = env("SQLX_OFFLINE")
.map(|s| s.eq_ignore_ascii_case("true") || s == "1")
.unwrap_or(false);
let database_url = env("DATABASE_URL").ok();
let var_name = Config::from_crate()
.common
.database_url_var();
let database_url = env(var_name).ok();
Metadata {
manifest_dir,

View File

@ -27,8 +27,7 @@ derive = ["sqlx-macros-core/derive"]
macros = ["sqlx-macros-core/macros"]
migrate = ["sqlx-macros-core/migrate"]
config-macros = ["sqlx-macros-core/config-macros"]
config-migrate = ["sqlx-macros-core/config-migrate"]
sqlx-toml = ["sqlx-macros-core/sqlx-toml"]
# database
mysql = ["sqlx-macros-core/mysql"]