From 8604b51ae3fcf46eb86e461c13428c2e3a7bebe7 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Thu, 19 Sep 2024 19:23:03 -0700 Subject: [PATCH] refactor(sqlx.toml): make all keys kebab-case, create `macros.preferred-crates` --- sqlx-core/src/config/common.rs | 14 ++- sqlx-core/src/config/macros.rs | 179 +++++++++++++++++++++------- sqlx-core/src/config/migrate.rs | 47 +++++--- sqlx-core/src/config/mod.rs | 6 +- sqlx-core/src/config/reference.toml | 42 ++++--- sqlx-core/src/config/tests.rs | 10 +- 6 files changed, 206 insertions(+), 92 deletions(-) diff --git a/sqlx-core/src/config/common.rs b/sqlx-core/src/config/common.rs index 1468f24a..c09ed80d 100644 --- a/sqlx-core/src/config/common.rs +++ b/sqlx-core/src/config/common.rs @@ -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` diff --git a/sqlx-core/src/config/macros.rs b/sqlx-core/src/config/macros.rs index 142f059d..9f4cf452 100644 --- a/sqlx-core/src/config/macros.rs +++ b/sqlx-core/src/config/macros.rs @@ -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, - /// 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>, + pub table_overrides: BTreeMap>, } -/// 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; /// 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; /// 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; /// 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 + } +} \ No newline at end of file diff --git a/sqlx-core/src/config/migrate.rs b/sqlx-core/src/config/migrate.rs index efc03a01..d0e55b35 100644 --- a/sqlx-core/src/config/migrate.rs +++ b/sqlx-core/src/config/migrate.rs @@ -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>, @@ -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, - /// 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 (`_.sql`). + /// Create non-reversible migrations (`_.sql`) by default. Simple, - /// Create a reversible migration (`_.up.sql` and `[...].down.sql`). + /// Create reversible migrations (`_.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", diff --git a/sqlx-core/src/config/mod.rs b/sqlx-core/src/config/mod.rs index 3bbde5c2..696752a5 100644 --- a/sqlx-core/src/config/mod.rs +++ b/sqlx-core/src/config/mod.rs @@ -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. /// diff --git a/sqlx-core/src/config/reference.toml b/sqlx-core/src/config/reference.toml index 6d52f615..e042824c 100644 --- a/sqlx-core/src/config/reference.toml +++ b/sqlx-core/src/config/reference.toml @@ -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 (`` | `\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" diff --git a/sqlx-core/src/config/tests.rs b/sqlx-core/src/config/tests.rs index e5033bb4..6c2883d5 100644 --- a/sqlx-core/src/config/tests.rs +++ b/sqlx-core/src/config/tests.rs @@ -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); }