Skip to content

Commit

Permalink
refactor: make Config always compiled
Browse files Browse the repository at this point in the history
simplifies usage while still making parsing optional for less generated code
  • Loading branch information
abonander committed Sep 18, 2024
1 parent 508983e commit 25c755b
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 65 deletions.
10 changes: 4 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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"]
Expand Down
5 changes: 4 additions & 1 deletion sqlx-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,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"]

Expand All @@ -65,6 +66,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"
Expand Down
12 changes: 7 additions & 5 deletions sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ features = ["offline"]

[features]
default = []
migrate = ["sha2", "crc", "config-migrate"]
migrate = ["sha2", "crc"]

any = []

Expand All @@ -30,11 +30,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
Expand Down
9 changes: 8 additions & 1 deletion sqlx-core/src/config/common.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand Down Expand Up @@ -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")
}
}
12 changes: 8 additions & 4 deletions sqlx-core/src/config/macros.rs
Original file line number Diff line number Diff line change
@@ -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.
///
Expand Down Expand Up @@ -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]
Expand Down
20 changes: 14 additions & 6 deletions sqlx-core/src/config/migrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down Expand Up @@ -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.
Expand All @@ -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:
///
Expand Down
95 changes: 63 additions & 32 deletions sqlx-core/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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
Expand All @@ -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.
///
Expand All @@ -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,
}

Expand All @@ -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,
Expand All @@ -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
}
}
}
Expand All @@ -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}")
}
}
})
}
Expand Down Expand Up @@ -188,19 +209,29 @@ 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));
}
};

// TODO: parse and lint TOML structure before deserializing
// 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))
}
}
}
2 changes: 0 additions & 2 deletions sqlx-core/src/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand Down Expand Up @@ -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::*;

Expand Down
1 change: 0 additions & 1 deletion sqlx-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ pub mod any;
#[cfg(feature = "migrate")]
pub mod testing;

#[cfg(feature = "config")]
pub mod config;

pub use error::{Error, Result};
Expand Down
4 changes: 1 addition & 3 deletions sqlx-macros-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,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"]
Expand Down
Loading

0 comments on commit 25c755b

Please sign in to comment.