refactor(sqlx.toml): make all keys kebab-case, create macros.preferred-crates

This commit is contained in:
Austin Bonander 2024-09-19 19:23:03 -07:00
parent 5cb3de38b9
commit 8604b51ae3
6 changed files with 206 additions and 92 deletions

View File

@ -1,6 +1,10 @@
/// Configuration shared by multiple components.
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct Config {
/// Override the database URL environment variable.
///
@ -17,14 +21,14 @@ pub struct Config {
///
/// #### `foo/sqlx.toml`
/// ```toml
/// [macros]
/// database_url_var = "FOO_DATABASE_URL"
/// [common]
/// database-url-var = "FOO_DATABASE_URL"
/// ```
///
/// #### `bar/sqlx.toml`
/// ```toml
/// [macros]
/// database_url_var = "BAR_DATABASE_URL"
/// [common]
/// database-url-var = "BAR_DATABASE_URL"
/// ```
///
/// #### `.env`

View File

@ -2,33 +2,16 @@ use std::collections::BTreeMap;
/// Configuration for the `query!()` family of macros.
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct Config {
/// Specify the crate to use for mapping date/time types to Rust.
///
/// The default behavior is to use whatever crate is enabled,
/// [`chrono`] or [`time`] (the latter takes precedent).
///
/// [`chrono`]: crate::types::chrono
/// [`time`]: crate::types::time
///
/// Example: Always Use Chrono
/// -------
/// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable
/// the `time` feature of SQLx which will force it on for all crates using SQLx,
/// which will result in problems if your crate wants to use types from [`chrono`].
///
/// You can use the type override syntax (see `sqlx::query!` for details),
/// or you can force an override globally by setting this option.
///
/// #### `sqlx.toml`
/// ```toml
/// [macros]
/// datetime_crate = "chrono"
/// ```
///
/// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
pub datetime_crate: DateTimeCrate,
/// Specify which crates' types to use when types from multiple crates apply.
///
/// See [`PreferredCrates`] for details.
pub preferred_crates: PreferredCrates,
/// Specify global overrides for mapping SQL type names to Rust type names.
///
@ -78,7 +61,7 @@ pub struct Config {
///
/// #### `sqlx.toml`
/// ```toml
/// [macros.type_overrides]
/// [macros.type-overrides]
/// # Override a built-in type
/// 'uuid' = "crate::types::MyUuid"
///
@ -115,7 +98,7 @@ pub struct Config {
///
/// #### `sqlx.toml`
/// ```toml
/// [macros.type_overrides]
/// [macros.type-overrides]
/// # Map SQL type `foo` to `crate::types::Foo`
/// 'foo' = "crate::types::Foo"
/// ```
@ -125,7 +108,7 @@ pub struct Config {
/// (See `Note` section above for details.)
///
/// ```toml
/// [macros.type_overrides]
/// [macros.type-overrides]
/// # Map SQL type `foo.foo` to `crate::types::Foo`
/// 'foo.foo' = "crate::types::Foo"
/// ```
@ -136,7 +119,7 @@ pub struct Config {
/// it must be wrapped in quotes _twice_ for SQLx to know the difference:
///
/// ```toml
/// [macros.type_overrides]
/// [macros.type-overrides]
/// # `"Foo"` in SQLx
/// '"Foo"' = "crate::types::Foo"
/// # **NOT** `"Foo"` in SQLx (parses as just `Foo`)
@ -151,7 +134,7 @@ pub struct Config {
/// (See `Note` section above for details.)
pub type_overrides: BTreeMap<SqlType, RustType>,
/// Specify per-column overrides for mapping SQL types to Rust types.
/// Specify per-table and per-column overrides for mapping SQL types to Rust types.
///
/// Default type mappings are defined by the database driver.
/// Refer to the `sqlx::types` module for details.
@ -206,7 +189,7 @@ pub struct Config {
///
/// #### `sqlx.toml`
/// ```toml
/// [macros.column_overrides.'foo']
/// [macros.table-overrides.'foo']
/// # Map column `bar` of table `foo` to Rust type `crate::types::Foo`:
/// 'bar' = "crate::types::Bar"
///
@ -218,23 +201,83 @@ pub struct Config {
/// # "Bar" = "crate::types::Bar"
///
/// # Table name may be quoted (note the wrapping single-quotes)
/// [macros.column_overrides.'"Foo"']
/// [macros.table-overrides.'"Foo"']
/// 'bar' = "crate::types::Bar"
/// '"Bar"' = "crate::types::Bar"
///
/// # Table name may also be schema-qualified.
/// # Note how the dot is inside the quotes.
/// [macros.column_overrides.'my_schema.my_table']
/// [macros.table-overrides.'my_schema.my_table']
/// 'my_column' = "crate::types::MyType"
///
/// # Quoted schema, table, and column names
/// [macros.column_overrides.'"My Schema"."My Table"']
/// [macros.table-overrides.'"My Schema"."My Table"']
/// '"My Column"' = "crate::types::MyType"
/// ```
pub column_overrides: BTreeMap<TableName, BTreeMap<ColumnName, RustType>>,
pub table_overrides: BTreeMap<TableName, BTreeMap<ColumnName, RustType>>,
}
/// The crate to use for mapping date/time types to Rust.
#[derive(Debug, Default)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "kebab-case")
)]
pub struct PreferredCrates {
/// Specify the crate to use for mapping date/time types to Rust.
///
/// The default behavior is to use whatever crate is enabled,
/// [`chrono`] or [`time`] (the latter takes precedent).
///
/// [`chrono`]: crate::types::chrono
/// [`time`]: crate::types::time
///
/// Example: Always Use Chrono
/// -------
/// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable
/// the `time` feature of SQLx which will force it on for all crates using SQLx,
/// which will result in problems if your crate wants to use types from [`chrono`].
///
/// You can use the type override syntax (see `sqlx::query!` for details),
/// or you can force an override globally by setting this option.
///
/// #### `sqlx.toml`
/// ```toml
/// [macros.preferred-crates]
/// date-time = "chrono"
/// ```
///
/// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
pub date_time: DateTimeCrate,
/// Specify the crate to use for mapping `NUMERIC` types to Rust.
///
/// The default behavior is to use whatever crate is enabled,
/// [`bigdecimal`] or [`rust_decimal`] (the latter takes precedent).
///
/// [`bigdecimal`]: crate::types::bigdecimal
/// [`rust_decimal`]: crate::types::rust_decimal
///
/// Example: Always Use `bigdecimal`
/// -------
/// Thanks to Cargo's [feature unification], a crate in the dependency graph may enable
/// the `rust_decimal` feature of SQLx which will force it on for all crates using SQLx,
/// which will result in problems if your crate wants to use types from [`bigdecimal`].
///
/// You can use the type override syntax (see `sqlx::query!` for details),
/// or you can force an override globally by setting this option.
///
/// #### `sqlx.toml`
/// ```toml
/// [macros.preferred-crates]
/// numeric = "bigdecimal"
/// ```
///
/// [feature unification]: https://doc.rust-lang.org/cargo/reference/features.html#feature-unification
pub numeric: NumericCrate,
}
/// The preferred crate to use for mapping date/time types to Rust.
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
@ -249,33 +292,63 @@ pub enum DateTimeCrate {
/// Always use types from [`chrono`][crate::types::chrono].
///
/// ```toml
/// [macros]
/// datetime_crate = "chrono"
/// [macros.preferred-crates]
/// date-time = "chrono"
/// ```
Chrono,
/// Always use types from [`time`][crate::types::time].
///
/// ```toml
/// [macros]
/// datetime_crate = "time"
/// [macros.preferred-crates]
/// date-time = "time"
/// ```
Time,
}
/// The preferred crate to use for mapping `NUMERIC` types to Rust.
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(rename_all = "snake_case")
)]
pub enum NumericCrate {
/// Use whichever crate is enabled (`rust_decimal` then `bigdecimal`).
#[default]
Inferred,
/// Always use types from [`bigdecimal`][crate::types::bigdecimal].
///
/// ```toml
/// [macros.preferred-crates]
/// numeric = "bigdecimal"
/// ```
#[cfg_attr(feature = "sqlx-toml", serde(rename = "bigdecimal"))]
BigDecimal,
/// Always use types from [`rust_decimal`][crate::types::rust_decimal].
///
/// ```toml
/// [macros.preferred-crates]
/// numeric = "rust_decimal"
/// ```
RustDecimal,
}
/// A SQL type name; may optionally be schema-qualified.
///
/// See [`macros.type_overrides`][Config::type_overrides] for usages.
/// See [`macros.type-overrides`][Config::type_overrides] for usages.
pub type SqlType = Box<str>;
/// A SQL table name; may optionally be schema-qualified.
///
/// See [`macros.column_overrides`][Config::column_overrides] for usages.
/// See [`macros.table-overrides`][Config::table_overrides] for usages.
pub type TableName = Box<str>;
/// A column in a SQL table.
///
/// See [`macros.column_overrides`][Config::column_overrides] for usages.
/// See [`macros.table-overrides`][Config::table_overrides] for usages.
pub type ColumnName = Box<str>;
/// A Rust type name or path.
@ -292,9 +365,25 @@ impl Config {
/// Get the override for a given column and table name (optionally schema-qualified).
pub fn column_override(&self, table: &str, column: &str) -> Option<&str> {
self.column_overrides
self.table_overrides
.get(table)
.and_then(|by_column| by_column.get(column))
.map(|s| &**s)
}
}
impl DateTimeCrate {
/// Returns `self == Self::Inferred`
#[inline(always)]
pub fn is_inferred(&self) -> bool {
*self == Self::Inferred
}
}
impl NumericCrate {
/// Returns `self == Self::Inferred`
#[inline(always)]
pub fn is_inferred(&self) -> bool {
*self == Self::Inferred
}
}

View File

@ -13,7 +13,11 @@ use std::collections::BTreeSet;
///
/// Be sure you know what you are doing and that you read all relevant documentation _thoroughly_.
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize), serde(default))]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct Config {
/// Override the name of the table used to track executed migrations.
///
@ -35,7 +39,7 @@ pub struct Config {
/// ```toml
/// [migrate]
/// # Put `_sqlx_migrations` in schema `foo`
/// table_name = "foo._sqlx_migrations"
/// table-name = "foo._sqlx_migrations"
/// ```
pub table_name: Option<Box<str>>,
@ -63,7 +67,7 @@ pub struct Config {
/// `sqlx.toml`:
/// ```toml
/// [migrate]
/// ignored_chars = ["\r"]
/// ignored-chars = ["\r"]
/// ```
///
/// For projects using Git, this can also be addressed using [`.gitattributes`]:
@ -91,33 +95,44 @@ pub struct Config {
/// ```toml
/// [migrate]
/// # Ignore common whitespace characters when hashing
/// ignored_chars = [" ", "\t", "\r", "\n"] # Space, tab, CR, LF
/// ignored-chars = [" ", "\t", "\r", "\n"] # Space, tab, CR, LF
/// ```
// Likely lower overhead for small sets than `HashSet`.
pub ignored_chars: BTreeSet<char>,
/// Specify the default type of migration that `sqlx migrate create` should create by default.
/// Specify default options for new migrations created with `sqlx migrate add`.
pub defaults: MigrationDefaults,
}
#[derive(Debug, Default)]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct MigrationDefaults {
/// Specify the default type of migration that `sqlx migrate add` should create by default.
///
/// ### Example: Use Reversible Migrations by Default
/// `sqlx.toml`:
/// ```toml
/// [migrate]
/// default_type = "reversible"
/// [migrate.defaults]
/// migration-type = "reversible"
/// ```
pub default_type: DefaultMigrationType,
pub migration_type: DefaultMigrationType,
/// Specify the default scheme that `sqlx migrate create` should use for version integers.
/// Specify the default scheme that `sqlx migrate add` should use for version integers.
///
/// ### Example: Use Sequential Versioning by Default
/// `sqlx.toml`:
/// ```toml
/// [migrate]
/// default_versioning = "sequential"
/// [migrate.defaults]
/// migration-versioning = "sequential"
/// ```
pub default_versioning: DefaultVersioning,
pub migration_versioning: DefaultVersioning,
}
/// The default type of migration that `sqlx migrate create` should create by default.
/// The default type of migration that `sqlx migrate add` should create by default.
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",
@ -130,14 +145,14 @@ pub enum DefaultMigrationType {
#[default]
Inferred,
/// Create a non-reversible migration (`<VERSION>_<DESCRIPTION>.sql`).
/// Create non-reversible migrations (`<VERSION>_<DESCRIPTION>.sql`) by default.
Simple,
/// Create a reversible migration (`<VERSION>_<DESCRIPTION>.up.sql` and `[...].down.sql`).
/// Create reversible migrations (`<VERSION>_<DESCRIPTION>.up.sql` and `[...].down.sql`) by default.
Reversible,
}
/// The default scheme that `sqlx migrate create` should use for version integers.
/// The default scheme that `sqlx migrate add` should use for version integers.
#[derive(Debug, Default, PartialEq, Eq)]
#[cfg_attr(
feature = "sqlx-toml",

View File

@ -45,7 +45,11 @@ mod tests;
/// The parsed structure of a `sqlx.toml` file.
#[derive(Debug, Default)]
#[cfg_attr(feature = "sqlx-toml", derive(serde::Deserialize))]
#[cfg_attr(
feature = "sqlx-toml",
derive(serde::Deserialize),
serde(default, rename_all = "kebab-case")
)]
pub struct Config {
/// Configuration shared by multiple components.
///

View File

@ -13,20 +13,24 @@
# This is used by both the macros and `sqlx-cli`.
#
# If not specified, defaults to `DATABASE_URL`
database_url_var = "FOO_DATABASE_URL"
database-url-var = "FOO_DATABASE_URL"
###############################################################################################
# Configuration for the `query!()` family of macros.
[macros]
[macros.preferred-crates]
# Force the macros to use the `chrono` crate for date/time types, even if `time` is enabled.
#
# Defaults to "inferred": use whichever crate is enabled (`time` takes precedence over `chrono`).
datetime_crate = "chrono"
date-time = "chrono"
# Or, ensure the macros always prefer `time`
# in case new date/time crates are added in the future:
# datetime_crate = "time"
# date-time = "time"
# Set global overrides for mapping SQL types to Rust types.
#
@ -38,7 +42,7 @@ datetime_crate = "chrono"
# ### Note: Orthogonal to Nullability
# These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>`
# or not. They only override the inner type used.
[macros.type_overrides]
[macros.type-overrides]
# Override a built-in type (map all `UUID` columns to `crate::types::MyUuid`)
'uuid' = "crate::types::MyUuid"
@ -67,7 +71,7 @@ datetime_crate = "chrono"
# Quoted schema and type name
'"Foo"."Bar"' = "crate::schema::foo::Bar"
# Set per-column overrides for mapping SQL types to Rust types.
# Set per-table and per-column overrides for mapping SQL types to Rust types.
#
# Note: table name is required in the header.
#
@ -76,7 +80,7 @@ datetime_crate = "chrono"
# ### Note: Orthogonal to Nullability
# These overrides do not affect whether `query!()` decides to wrap a column in `Option<_>`
# or not. They only override the inner type used.
[macros.column_overrides.'foo']
[macros.table-overrides.'foo']
# Map column `bar` of table `foo` to Rust type `crate::types::Foo`:
'bar' = "crate::types::Bar"
@ -88,17 +92,17 @@ datetime_crate = "chrono"
# "Bar" = "crate::types::Bar"
# Table name may be quoted (note the wrapping single-quotes)
[macros.column_overrides.'"Foo"']
[macros.table-overrides.'"Foo"']
'bar' = "crate::types::Bar"
'"Bar"' = "crate::types::Bar"
# Table name may also be schema-qualified.
# Note how the dot is inside the quotes.
[macros.column_overrides.'my_schema.my_table']
[macros.table-overrides.'my_schema.my_table']
'my_column' = "crate::types::MyType"
# Quoted schema, table, and column names
[macros.column_overrides.'"My Schema"."My Table"']
[macros.table-overrides.'"My Schema"."My Table"']
'"My Column"' = "crate::types::MyType"
###############################################################################################
@ -130,12 +134,12 @@ datetime_crate = "chrono"
# You should create the new table as a copy of the existing migrations table (with contents!),
# and be sure all instances of your application have been migrated to the new
# table before deleting the old one.
table_name = "foo._sqlx_migrations"
table-name = "foo._sqlx_migrations"
# Override the directory used for migrations files.
#
# Relative to the crate root for `sqlx::migrate!()`, or the current directory for `sqlx-cli`.
migrations_dir = "foo/migrations"
migrations-dir = "foo/migrations"
# Specify characters that should be ignored when hashing migrations.
#
@ -148,32 +152,34 @@ migrations_dir = "foo/migrations"
# change the output of the hash.
#
# This may require manual rectification for deployed databases.
# ignored_chars = []
# ignored-chars = []
# Ignore Carriage Returns (`<CR>` | `\r`)
# Note that the TOML format requires double-quoted strings to process escapes.
# ignored_chars = ["\r"]
# ignored-chars = ["\r"]
# Ignore common whitespace characters (beware syntatically significant whitespace!)
# Space, tab, CR, LF, zero-width non-breaking space (U+FEFF)
#
# U+FEFF is added by some editors as a magic number at the beginning of a text file indicating it is UTF-8 encoded,
# where it is known as a byte-order mark (BOM): https://en.wikipedia.org/wiki/Byte_order_mark
ignored_chars = [" ", "\t", "\r", "\n", "\uFEFF"]
ignored-chars = [" ", "\t", "\r", "\n", "\uFEFF"]
# Set default options for new migrations.
[migrate.defaults]
# Specify reversible migrations by default (for `sqlx migrate create`).
#
# Defaults to "inferred": uses the type of the last migration, or "simple" otherwise.
default_type = "reversible"
migration-type = "reversible"
# Specify simple (non-reversible) migrations by default.
# default_type = "simple"
# migration-type = "simple"
# Specify sequential versioning by default (for `sqlx migrate create`).
#
# Defaults to "inferred": guesses the versioning scheme from the latest migrations,
# or "timestamp" otherwise.
default_versioning = "sequential"
migration-versioning = "sequential"
# Specify timestamp versioning by default.
# default_versioning = "timestamp"
# migration-versioning = "timestamp"

View File

@ -8,11 +8,7 @@ fn reference_parses_as_config() {
.unwrap_or_else(|e| panic!("expected reference.toml to parse as Config: {e}"));
assert_common_config(&config.common);
#[cfg(feature = "config-macros")]
assert_macros_config(&config.macros);
#[cfg(feature = "config-migrate")]
assert_migrate_config(&config.migrate);
}
@ -23,7 +19,7 @@ fn assert_common_config(config: &config::common::Config) {
fn assert_macros_config(config: &config::macros::Config) {
use config::macros::*;
assert_eq!(config.datetime_crate, DateTimeCrate::Chrono);
assert_eq!(config.preferred_crates.date_time, DateTimeCrate::Chrono);
// Type overrides
// Don't need to cover everything, just some important canaries.
@ -83,6 +79,6 @@ fn assert_migrate_config(config: &config::migrate::Config) {
assert_eq!(config.ignored_chars, ignored_chars);
assert_eq!(config.default_type, DefaultMigrationType::Reversible);
assert_eq!(config.default_versioning, DefaultVersioning::Sequential);
assert_eq!(config.defaults.migration_type, DefaultMigrationType::Reversible);
assert_eq!(config.defaults.migration_versioning, DefaultVersioning::Sequential);
}