From bdf984ce8a066bd53638d4d29dfcd3edaca22790 Mon Sep 17 00:00:00 2001 From: DawsonCodes <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:27:41 +0300 Subject: [PATCH 01/10] config --- sqlx-cli/src/bin/sqlx.rs | 3 ++- sqlx-cli/src/config.rs | 38 ++++++++++++++++++++++++++++++++++++++ sqlx-cli/src/lib.rs | 26 +++++++++++++++----------- sqlx-cli/src/opt.rs | 13 ++++++++++++- 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 sqlx-cli/src/config.rs diff --git a/sqlx-cli/src/bin/sqlx.rs b/sqlx-cli/src/bin/sqlx.rs index 59025cd7da..9e191020e2 100644 --- a/sqlx-cli/src/bin/sqlx.rs +++ b/sqlx-cli/src/bin/sqlx.rs @@ -1,10 +1,11 @@ use clap::Parser; use console::style; -use sqlx_cli::Opt; +use sqlx_cli::{config::SqlxConfig, Opt}; #[tokio::main] async fn main() { dotenvy::dotenv().ok(); + SqlxConfig::read(); // no special handling here if let Err(error) = sqlx_cli::run(Opt::parse()).await { println!("{} {}", style("error:").bold().red(), error); diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs new file mode 100644 index 0000000000..36b2894a62 --- /dev/null +++ b/sqlx-cli/src/config.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; +use std::fs; + +/// Main struct for the sqlx-config.json +#[derive(Debug, Serialize, Deserialize)] +pub struct SqlxConfig { + pub env_path: String, +} + +impl Default for SqlxConfig { + fn default() -> Self { + SqlxConfig { + env_path: ".env".to_string(), + } + } +} + +impl SqlxConfig { + pub fn read() -> anyhow::Result { + let file = fs::read_to_string("sqlx-config.json"); + match file { + Ok(f) => { + let config: SqlxConfig = serde_json::from_str(&f).unwrap(); + Ok(config) + } + Err(e) => { + // Create the file + let default = SqlxConfig::default(); + + let json = serde_json::to_string(&default)?; + + fs::write("sqlx-config.json", json)?; + + Ok(SqlxConfig::default()) + } + } + } +} diff --git a/sqlx-cli/src/lib.rs b/sqlx-cli/src/lib.rs index a0890fe053..6687c33cce 100644 --- a/sqlx-cli/src/lib.rs +++ b/sqlx-cli/src/lib.rs @@ -14,11 +14,13 @@ mod metadata; // mod migrator; #[cfg(feature = "completions")] mod completions; +mod config; mod migrate; mod opt; mod prepare; pub use crate::opt::Opt; +pub use config::SqlxConfig; pub async fn run(opt: Opt) -> Result<()> { match opt.command { @@ -70,22 +72,24 @@ pub async fn run(opt: Opt) -> Result<()> { }, Command::Database(database) => match database.command { - DatabaseCommand::Create { connect_opts } => database::create(&connect_opts).await?, + DatabaseCommand::Create { mut connect_opts } => { + database::create(&mut connect_opts).await? + } DatabaseCommand::Drop { confirmation, - connect_opts, + mut connect_opts, force, - } => database::drop(&connect_opts, !confirmation.yes, force).await?, + } => database::drop(&mut connect_opts, !confirmation.yes, force).await?, DatabaseCommand::Reset { confirmation, source, - connect_opts, + mut connect_opts, force, - } => database::reset(&source, &connect_opts, !confirmation.yes, force).await?, + } => database::reset(&source, &mut connect_opts, !confirmation.yes, force).await?, DatabaseCommand::Setup { source, - connect_opts, - } => database::setup(&source, &connect_opts).await?, + mut connect_opts, + } => database::setup(&source, &mut connect_opts).await?, }, Command::Prepare { @@ -103,7 +107,7 @@ pub async fn run(opt: Opt) -> Result<()> { } /// Attempt to connect to the database server, retrying up to `ops.connect_timeout`. -async fn connect(opts: &ConnectOpts) -> anyhow::Result { +async fn connect(opts: &mut ConnectOpts) -> anyhow::Result { retry_connect_errors(opts, AnyConnection::connect).await } @@ -116,19 +120,19 @@ async fn retry_connect_errors<'a, F, Fut, T>( mut connect: F, ) -> anyhow::Result where - F: FnMut(&'a str) -> Fut, + F: FnMut(&str) -> Fut, Fut: Future> + 'a, { sqlx::any::install_default_drivers(); - let db_url = opts.required_db_url()?; + let db_url = opts.required_db_url()?.to_string(); backoff::future::retry( backoff::ExponentialBackoffBuilder::new() .with_max_elapsed_time(Some(Duration::from_secs(opts.connect_timeout))) .build(), || { - connect(db_url).map_err(|e| -> backoff::Error { + connect(&db_url).map_err(|e| -> backoff::Error { match e { sqlx::Error::Io(ref ioe) => match ioe.kind() { io::ErrorKind::ConnectionRefused diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index be9ca6dac1..b4eec4cf3f 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -246,6 +246,9 @@ pub struct ConnectOpts { #[clap(long, default_value = "10")] pub connect_timeout: u64, + #[clap(long, default_value = ".env")] + pub env_path: Option, + /// Set whether or not to create SQLite databases in Write-Ahead Log (WAL) mode: /// https://www.sqlite.org/wal.html /// @@ -262,7 +265,15 @@ pub struct ConnectOpts { impl ConnectOpts { /// Require a database URL to be provided, otherwise /// return an error. - pub fn required_db_url(&self) -> anyhow::Result<&str> { + pub fn required_db_url(&mut self) -> anyhow::Result<&str> { + if self.env_path.is_some() { + dotenvy::from_filename(&self.env_path.as_deref().unwrap()).ok(); + + let url = dotenvy::var("DATABASE_URL").ok(); + + self.database_url = url; + } + self.database_url.as_deref().ok_or_else( || anyhow::anyhow!( "the `--database-url` option the or `DATABASE_URL` environment variable must be provided" From fa732aba4df4331e665fd423589b91fa26d25e8d Mon Sep 17 00:00:00 2001 From: DawsonCodes <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:27:44 +0300 Subject: [PATCH 02/10] changes --- sqlx-cli/sqlx-config.schema.json | 13 +++++++++++++ sqlx-cli/src/config.rs | 6 +++++- sqlx-cli/src/database.rs | 12 ++++++++---- sqlx-cli/src/opt.rs | 2 +- sqlx-cli/src/prepare.rs | 16 ++++++++-------- sqlx.config.json | 3 +++ 6 files changed, 38 insertions(+), 14 deletions(-) create mode 100644 sqlx-cli/sqlx-config.schema.json create mode 100644 sqlx.config.json diff --git a/sqlx-cli/sqlx-config.schema.json b/sqlx-cli/sqlx-config.schema.json new file mode 100644 index 0000000000..4f2fa83d93 --- /dev/null +++ b/sqlx-cli/sqlx-config.schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "env_path": { + "type": "string", + "description": "Path to the .env file", + "default": ".env", + "examples": ["../.env", "../myapp/.env"] + } + }, + "required": ["database_url"] +} diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index 36b2894a62..e3073da53d 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -4,12 +4,16 @@ use std::fs; /// Main struct for the sqlx-config.json #[derive(Debug, Serialize, Deserialize)] pub struct SqlxConfig { + #[serde(rename = "$schema")] + pub schema: String, pub env_path: String, } impl Default for SqlxConfig { fn default() -> Self { SqlxConfig { + schema: "https://github.com/dawsoncodes/sqlx/sqlx-cli/sqlx-config.schema.json" + .to_string(), env_path: ".env".to_string(), } } @@ -31,7 +35,7 @@ impl SqlxConfig { fs::write("sqlx-config.json", json)?; - Ok(SqlxConfig::default()) + Ok(default) } } } diff --git a/sqlx-cli/src/database.rs b/sqlx-cli/src/database.rs index 82dcfcfed9..702435fab9 100644 --- a/sqlx-cli/src/database.rs +++ b/sqlx-cli/src/database.rs @@ -5,7 +5,7 @@ use promptly::{prompt, ReadlineError}; use sqlx::any::Any; use sqlx::migrate::MigrateDatabase; -pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { +pub async fn create(connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { // NOTE: only retry the idempotent action. // We're assuming that if this succeeds, then any following operations should also succeed. let exists = crate::retry_connect_errors(connect_opts, Any::database_exists).await?; @@ -23,7 +23,11 @@ pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { Ok(()) } -pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> { +pub async fn drop( + connect_opts: &mut ConnectOpts, + confirm: bool, + force: bool, +) -> anyhow::Result<()> { if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) { return Ok(()); } @@ -45,7 +49,7 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> any pub async fn reset( migration_source: &str, - connect_opts: &ConnectOpts, + connect_opts: &mut ConnectOpts, confirm: bool, force: bool, ) -> anyhow::Result<()> { @@ -53,7 +57,7 @@ pub async fn reset( setup(migration_source, connect_opts).await } -pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> { +pub async fn setup(migration_source: &str, connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { create(connect_opts).await?; migrate::run(migration_source, connect_opts, false, false, None).await } diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index b4eec4cf3f..62876bd766 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -265,7 +265,7 @@ pub struct ConnectOpts { impl ConnectOpts { /// Require a database URL to be provided, otherwise /// return an error. - pub fn required_db_url(&mut self) -> anyhow::Result<&str> { + pub fn required_db_url(&self) -> anyhow::Result<&str> { if self.env_path.is_some() { dotenvy::from_filename(&self.env_path.as_deref().unwrap()).ok(); diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index 18987048f9..db655fbc80 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -47,7 +47,7 @@ hint: This command only works in the manifest directory of a Cargo package or wo ); let metadata: Metadata = Metadata::from_current_directory(&cargo)?; - let ctx = PrepareCtx { + let mut ctx = PrepareCtx { workspace, cargo, cargo_args, @@ -56,15 +56,15 @@ hint: This command only works in the manifest directory of a Cargo package or wo }; if check { - prepare_check(&ctx).await + prepare_check(&mut ctx).await } else { - prepare(&ctx).await + prepare(&mut ctx).await } } -async fn prepare(ctx: &PrepareCtx) -> anyhow::Result<()> { +async fn prepare(ctx: &mut PrepareCtx) -> anyhow::Result<()> { if ctx.connect_opts.database_url.is_some() { - check_backend(&ctx.connect_opts).await?; + check_backend(&mut ctx.connect_opts).await?; } let prepare_dir = ctx.prepare_dir()?; @@ -90,9 +90,9 @@ async fn prepare(ctx: &PrepareCtx) -> anyhow::Result<()> { Ok(()) } -async fn prepare_check(ctx: &PrepareCtx) -> anyhow::Result<()> { +async fn prepare_check(ctx: &mut PrepareCtx) -> anyhow::Result<()> { if ctx.connect_opts.database_url.is_some() { - check_backend(&ctx.connect_opts).await?; + check_backend(&mut ctx.connect_opts).await?; } // Re-generate and store the queries in a separate directory from both the prepared @@ -348,7 +348,7 @@ fn load_json_file(path: impl AsRef) -> anyhow::Result { Ok(serde_json::from_slice(&file_bytes)?) } -async fn check_backend(opts: &ConnectOpts) -> anyhow::Result<()> { +async fn check_backend(opts: &mut ConnectOpts) -> anyhow::Result<()> { crate::connect(opts).await?.close().await?; Ok(()) } diff --git a/sqlx.config.json b/sqlx.config.json new file mode 100644 index 0000000000..37a8e30085 --- /dev/null +++ b/sqlx.config.json @@ -0,0 +1,3 @@ +{ + "$schema": "sqlx-cli/sqlx-config.schema.json" +} From f714dc423cdbc20dab52a143a26d3c713635ca9a Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:52:46 +0300 Subject: [PATCH 03/10] config init --- sqlx-cli/src/bin/sqlx.rs | 3 ++- sqlx-cli/src/config.rs | 38 ++++++++++++++++++++++++++++++++++++++ sqlx-cli/src/lib.rs | 26 +++++++++++++++----------- sqlx-cli/src/opt.rs | 13 ++++++++++++- 4 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 sqlx-cli/src/config.rs diff --git a/sqlx-cli/src/bin/sqlx.rs b/sqlx-cli/src/bin/sqlx.rs index 59025cd7da..9e191020e2 100644 --- a/sqlx-cli/src/bin/sqlx.rs +++ b/sqlx-cli/src/bin/sqlx.rs @@ -1,10 +1,11 @@ use clap::Parser; use console::style; -use sqlx_cli::Opt; +use sqlx_cli::{config::SqlxConfig, Opt}; #[tokio::main] async fn main() { dotenvy::dotenv().ok(); + SqlxConfig::read(); // no special handling here if let Err(error) = sqlx_cli::run(Opt::parse()).await { println!("{} {}", style("error:").bold().red(), error); diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs new file mode 100644 index 0000000000..36b2894a62 --- /dev/null +++ b/sqlx-cli/src/config.rs @@ -0,0 +1,38 @@ +use serde::{Deserialize, Serialize}; +use std::fs; + +/// Main struct for the sqlx-config.json +#[derive(Debug, Serialize, Deserialize)] +pub struct SqlxConfig { + pub env_path: String, +} + +impl Default for SqlxConfig { + fn default() -> Self { + SqlxConfig { + env_path: ".env".to_string(), + } + } +} + +impl SqlxConfig { + pub fn read() -> anyhow::Result { + let file = fs::read_to_string("sqlx-config.json"); + match file { + Ok(f) => { + let config: SqlxConfig = serde_json::from_str(&f).unwrap(); + Ok(config) + } + Err(e) => { + // Create the file + let default = SqlxConfig::default(); + + let json = serde_json::to_string(&default)?; + + fs::write("sqlx-config.json", json)?; + + Ok(SqlxConfig::default()) + } + } + } +} diff --git a/sqlx-cli/src/lib.rs b/sqlx-cli/src/lib.rs index a0890fe053..6687c33cce 100644 --- a/sqlx-cli/src/lib.rs +++ b/sqlx-cli/src/lib.rs @@ -14,11 +14,13 @@ mod metadata; // mod migrator; #[cfg(feature = "completions")] mod completions; +mod config; mod migrate; mod opt; mod prepare; pub use crate::opt::Opt; +pub use config::SqlxConfig; pub async fn run(opt: Opt) -> Result<()> { match opt.command { @@ -70,22 +72,24 @@ pub async fn run(opt: Opt) -> Result<()> { }, Command::Database(database) => match database.command { - DatabaseCommand::Create { connect_opts } => database::create(&connect_opts).await?, + DatabaseCommand::Create { mut connect_opts } => { + database::create(&mut connect_opts).await? + } DatabaseCommand::Drop { confirmation, - connect_opts, + mut connect_opts, force, - } => database::drop(&connect_opts, !confirmation.yes, force).await?, + } => database::drop(&mut connect_opts, !confirmation.yes, force).await?, DatabaseCommand::Reset { confirmation, source, - connect_opts, + mut connect_opts, force, - } => database::reset(&source, &connect_opts, !confirmation.yes, force).await?, + } => database::reset(&source, &mut connect_opts, !confirmation.yes, force).await?, DatabaseCommand::Setup { source, - connect_opts, - } => database::setup(&source, &connect_opts).await?, + mut connect_opts, + } => database::setup(&source, &mut connect_opts).await?, }, Command::Prepare { @@ -103,7 +107,7 @@ pub async fn run(opt: Opt) -> Result<()> { } /// Attempt to connect to the database server, retrying up to `ops.connect_timeout`. -async fn connect(opts: &ConnectOpts) -> anyhow::Result { +async fn connect(opts: &mut ConnectOpts) -> anyhow::Result { retry_connect_errors(opts, AnyConnection::connect).await } @@ -116,19 +120,19 @@ async fn retry_connect_errors<'a, F, Fut, T>( mut connect: F, ) -> anyhow::Result where - F: FnMut(&'a str) -> Fut, + F: FnMut(&str) -> Fut, Fut: Future> + 'a, { sqlx::any::install_default_drivers(); - let db_url = opts.required_db_url()?; + let db_url = opts.required_db_url()?.to_string(); backoff::future::retry( backoff::ExponentialBackoffBuilder::new() .with_max_elapsed_time(Some(Duration::from_secs(opts.connect_timeout))) .build(), || { - connect(db_url).map_err(|e| -> backoff::Error { + connect(&db_url).map_err(|e| -> backoff::Error { match e { sqlx::Error::Io(ref ioe) => match ioe.kind() { io::ErrorKind::ConnectionRefused diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index be9ca6dac1..b4eec4cf3f 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -246,6 +246,9 @@ pub struct ConnectOpts { #[clap(long, default_value = "10")] pub connect_timeout: u64, + #[clap(long, default_value = ".env")] + pub env_path: Option, + /// Set whether or not to create SQLite databases in Write-Ahead Log (WAL) mode: /// https://www.sqlite.org/wal.html /// @@ -262,7 +265,15 @@ pub struct ConnectOpts { impl ConnectOpts { /// Require a database URL to be provided, otherwise /// return an error. - pub fn required_db_url(&self) -> anyhow::Result<&str> { + pub fn required_db_url(&mut self) -> anyhow::Result<&str> { + if self.env_path.is_some() { + dotenvy::from_filename(&self.env_path.as_deref().unwrap()).ok(); + + let url = dotenvy::var("DATABASE_URL").ok(); + + self.database_url = url; + } + self.database_url.as_deref().ok_or_else( || anyhow::anyhow!( "the `--database-url` option the or `DATABASE_URL` environment variable must be provided" From f98f773789cbe40d9dbe9453f403162b6288d08e Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:53:56 +0300 Subject: [PATCH 04/10] sqlx config schema --- sqlx-cli/sqlx-config.schema.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 sqlx-cli/sqlx-config.schema.json diff --git a/sqlx-cli/sqlx-config.schema.json b/sqlx-cli/sqlx-config.schema.json new file mode 100644 index 0000000000..de5e42a57c --- /dev/null +++ b/sqlx-cli/sqlx-config.schema.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "env_path": { + "type": "string", + "description": "Path to the .env file", + "default": ".env", + "examples": ["../.env", "../myapp/.env"] + } + } +} From c46dacdc42fc2c9fadff1927a130c87475d6d418 Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:54:35 +0300 Subject: [PATCH 05/10] made SqlxConfig and ConnectOpts mutable --- sqlx-cli/src/bin/sqlx.rs | 2 +- sqlx-cli/src/config.rs | 8 ++++++-- sqlx-cli/src/database.rs | 12 ++++++++---- sqlx-cli/src/lib.rs | 22 ++++++++++++---------- sqlx-cli/src/migrate.rs | 10 +++++----- sqlx-cli/src/opt.rs | 2 +- sqlx-cli/src/prepare.rs | 16 ++++++++-------- 7 files changed, 41 insertions(+), 31 deletions(-) diff --git a/sqlx-cli/src/bin/sqlx.rs b/sqlx-cli/src/bin/sqlx.rs index 9e191020e2..86b991eb27 100644 --- a/sqlx-cli/src/bin/sqlx.rs +++ b/sqlx-cli/src/bin/sqlx.rs @@ -1,6 +1,6 @@ use clap::Parser; use console::style; -use sqlx_cli::{config::SqlxConfig, Opt}; +use sqlx_cli::{Opt, SqlxConfig}; #[tokio::main] async fn main() { diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index 36b2894a62..4885cf8c11 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -4,12 +4,16 @@ use std::fs; /// Main struct for the sqlx-config.json #[derive(Debug, Serialize, Deserialize)] pub struct SqlxConfig { + #[serde(rename = "$schema")] + pub schema: String, pub env_path: String, } impl Default for SqlxConfig { fn default() -> Self { SqlxConfig { + schema: "https://github.com/dawsoncodes/sqlx/sqlx-cli/sqlx-config.schema.json" + .to_string(), env_path: ".env".to_string(), } } @@ -23,7 +27,7 @@ impl SqlxConfig { let config: SqlxConfig = serde_json::from_str(&f).unwrap(); Ok(config) } - Err(e) => { + Err(_) => { // Create the file let default = SqlxConfig::default(); @@ -31,7 +35,7 @@ impl SqlxConfig { fs::write("sqlx-config.json", json)?; - Ok(SqlxConfig::default()) + Ok(default) } } } diff --git a/sqlx-cli/src/database.rs b/sqlx-cli/src/database.rs index 82dcfcfed9..702435fab9 100644 --- a/sqlx-cli/src/database.rs +++ b/sqlx-cli/src/database.rs @@ -5,7 +5,7 @@ use promptly::{prompt, ReadlineError}; use sqlx::any::Any; use sqlx::migrate::MigrateDatabase; -pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { +pub async fn create(connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { // NOTE: only retry the idempotent action. // We're assuming that if this succeeds, then any following operations should also succeed. let exists = crate::retry_connect_errors(connect_opts, Any::database_exists).await?; @@ -23,7 +23,11 @@ pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { Ok(()) } -pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> { +pub async fn drop( + connect_opts: &mut ConnectOpts, + confirm: bool, + force: bool, +) -> anyhow::Result<()> { if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) { return Ok(()); } @@ -45,7 +49,7 @@ pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> any pub async fn reset( migration_source: &str, - connect_opts: &ConnectOpts, + connect_opts: &mut ConnectOpts, confirm: bool, force: bool, ) -> anyhow::Result<()> { @@ -53,7 +57,7 @@ pub async fn reset( setup(migration_source, connect_opts).await } -pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> { +pub async fn setup(migration_source: &str, connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { create(connect_opts).await?; migrate::run(migration_source, connect_opts, false, false, None).await } diff --git a/sqlx-cli/src/lib.rs b/sqlx-cli/src/lib.rs index 6687c33cce..55ea66a8d9 100644 --- a/sqlx-cli/src/lib.rs +++ b/sqlx-cli/src/lib.rs @@ -36,12 +36,12 @@ pub async fn run(opt: Opt) -> Result<()> { source, dry_run, ignore_missing, - connect_opts, + mut connect_opts, target_version, } => { migrate::run( &source, - &connect_opts, + &mut connect_opts, dry_run, *ignore_missing, target_version, @@ -52,12 +52,12 @@ pub async fn run(opt: Opt) -> Result<()> { source, dry_run, ignore_missing, - connect_opts, + mut connect_opts, target_version, } => { migrate::revert( &source, - &connect_opts, + &mut connect_opts, dry_run, *ignore_missing, target_version, @@ -66,8 +66,8 @@ pub async fn run(opt: Opt) -> Result<()> { } MigrateCommand::Info { source, - connect_opts, - } => migrate::info(&source, &connect_opts).await?, + mut connect_opts, + } => migrate::info(&source, &mut connect_opts).await?, MigrateCommand::BuildScript { source, force } => migrate::build_script(&source, force)?, }, @@ -116,20 +116,22 @@ async fn connect(opts: &mut ConnectOpts) -> anyhow::Result { /// /// The closure is passed `&ops.database_url` for easy composition. async fn retry_connect_errors<'a, F, Fut, T>( - opts: &'a ConnectOpts, + opts: &'a mut ConnectOpts, mut connect: F, ) -> anyhow::Result where - F: FnMut(&str) -> Fut, + F: FnMut(&'a str) -> Fut, Fut: Future> + 'a, { sqlx::any::install_default_drivers(); - let db_url = opts.required_db_url()?.to_string(); + let opts_clone = opts.clone(); + + let db_url = opts.required_db_url()?; backoff::future::retry( backoff::ExponentialBackoffBuilder::new() - .with_max_elapsed_time(Some(Duration::from_secs(opts.connect_timeout))) + .with_max_elapsed_time(Some(Duration::from_secs(opts_clone.connect_timeout))) .build(), || { connect(&db_url).map_err(|e| -> backoff::Error { diff --git a/sqlx-cli/src/migrate.rs b/sqlx-cli/src/migrate.rs index 1ab8929fc1..31c6438e03 100644 --- a/sqlx-cli/src/migrate.rs +++ b/sqlx-cli/src/migrate.rs @@ -180,9 +180,9 @@ fn short_checksum(checksum: &[u8]) -> String { s } -pub async fn info(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> { +pub async fn info(migration_source: &str, connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { let migrator = Migrator::new(Path::new(migration_source)).await?; - let mut conn = crate::connect(&connect_opts).await?; + let mut conn = crate::connect(connect_opts).await?; conn.ensure_migrations_table().await?; @@ -261,7 +261,7 @@ fn validate_applied_migrations( pub async fn run( migration_source: &str, - connect_opts: &ConnectOpts, + connect_opts: &mut ConnectOpts, dry_run: bool, ignore_missing: bool, target_version: Option, @@ -356,7 +356,7 @@ pub async fn run( pub async fn revert( migration_source: &str, - connect_opts: &ConnectOpts, + connect_opts: &mut ConnectOpts, dry_run: bool, ignore_missing: bool, target_version: Option, @@ -368,7 +368,7 @@ pub async fn revert( } } - let mut conn = crate::connect(&connect_opts).await?; + let mut conn = crate::connect(connect_opts).await?; conn.ensure_migrations_table().await?; diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index b4eec4cf3f..16a3e8bbf8 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -235,7 +235,7 @@ impl Deref for Source { } /// Argument for the database URL. -#[derive(Args, Debug)] +#[derive(Args, Debug, Clone)] pub struct ConnectOpts { /// Location of the DB, by default will be read from the DATABASE_URL env var or `.env` files. #[clap(long, short = 'D', env)] diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index 18987048f9..db655fbc80 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -47,7 +47,7 @@ hint: This command only works in the manifest directory of a Cargo package or wo ); let metadata: Metadata = Metadata::from_current_directory(&cargo)?; - let ctx = PrepareCtx { + let mut ctx = PrepareCtx { workspace, cargo, cargo_args, @@ -56,15 +56,15 @@ hint: This command only works in the manifest directory of a Cargo package or wo }; if check { - prepare_check(&ctx).await + prepare_check(&mut ctx).await } else { - prepare(&ctx).await + prepare(&mut ctx).await } } -async fn prepare(ctx: &PrepareCtx) -> anyhow::Result<()> { +async fn prepare(ctx: &mut PrepareCtx) -> anyhow::Result<()> { if ctx.connect_opts.database_url.is_some() { - check_backend(&ctx.connect_opts).await?; + check_backend(&mut ctx.connect_opts).await?; } let prepare_dir = ctx.prepare_dir()?; @@ -90,9 +90,9 @@ async fn prepare(ctx: &PrepareCtx) -> anyhow::Result<()> { Ok(()) } -async fn prepare_check(ctx: &PrepareCtx) -> anyhow::Result<()> { +async fn prepare_check(ctx: &mut PrepareCtx) -> anyhow::Result<()> { if ctx.connect_opts.database_url.is_some() { - check_backend(&ctx.connect_opts).await?; + check_backend(&mut ctx.connect_opts).await?; } // Re-generate and store the queries in a separate directory from both the prepared @@ -348,7 +348,7 @@ fn load_json_file(path: impl AsRef) -> anyhow::Result { Ok(serde_json::from_slice(&file_bytes)?) } -async fn check_backend(opts: &ConnectOpts) -> anyhow::Result<()> { +async fn check_backend(opts: &mut ConnectOpts) -> anyhow::Result<()> { crate::connect(opts).await?.close().await?; Ok(()) } From 7de27bb060daf775306674ca05629b281bf3279f Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 05:59:09 +0300 Subject: [PATCH 06/10] sqlx config read on cli run --- sqlx-cli/src/bin/cargo-sqlx.rs | 6 +++++- sqlx-cli/src/bin/sqlx.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/sqlx-cli/src/bin/cargo-sqlx.rs b/sqlx-cli/src/bin/cargo-sqlx.rs index 58f7b345f4..eceb11701c 100644 --- a/sqlx-cli/src/bin/cargo-sqlx.rs +++ b/sqlx-cli/src/bin/cargo-sqlx.rs @@ -1,6 +1,6 @@ use clap::Parser; use console::style; -use sqlx_cli::Opt; +use sqlx_cli::{Opt, SqlxConfig}; use std::process; // cargo invokes this binary as `cargo-sqlx sqlx ` @@ -15,6 +15,10 @@ enum Cli { async fn main() { dotenvy::dotenv().ok(); let Cli::Sqlx(opt) = Cli::parse(); + SqlxConfig::read() + .ok() + .ok_or(anyhow::anyhow!("Error reading sqlx-config.file")) + .unwrap(); if let Err(error) = sqlx_cli::run(opt).await { println!("{} {}", style("error:").bold().red(), error); diff --git a/sqlx-cli/src/bin/sqlx.rs b/sqlx-cli/src/bin/sqlx.rs index 86b991eb27..3d0bd6c25f 100644 --- a/sqlx-cli/src/bin/sqlx.rs +++ b/sqlx-cli/src/bin/sqlx.rs @@ -5,7 +5,11 @@ use sqlx_cli::{Opt, SqlxConfig}; #[tokio::main] async fn main() { dotenvy::dotenv().ok(); - SqlxConfig::read(); + SqlxConfig::read() + .ok() + .ok_or(anyhow::anyhow!("Error reading sqlx-config.file")) + .unwrap(); + // no special handling here if let Err(error) = sqlx_cli::run(Opt::parse()).await { println!("{} {}", style("error:").bold().red(), error); From 5c4d861a488d7b687ed853cc6b89d0150c43a9b7 Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 06:03:35 +0300 Subject: [PATCH 07/10] parsing default config --- sqlx-cli/src/bin/cargo-sqlx.rs | 3 ++- sqlx-cli/src/config.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sqlx-cli/src/bin/cargo-sqlx.rs b/sqlx-cli/src/bin/cargo-sqlx.rs index eceb11701c..dd0a6ae94e 100644 --- a/sqlx-cli/src/bin/cargo-sqlx.rs +++ b/sqlx-cli/src/bin/cargo-sqlx.rs @@ -14,12 +14,13 @@ enum Cli { #[tokio::main] async fn main() { dotenvy::dotenv().ok(); - let Cli::Sqlx(opt) = Cli::parse(); SqlxConfig::read() .ok() .ok_or(anyhow::anyhow!("Error reading sqlx-config.file")) .unwrap(); + let Cli::Sqlx(opt) = Cli::parse(); + if let Err(error) = sqlx_cli::run(opt).await { println!("{} {}", style("error:").bold().red(), error); process::exit(1); diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index 4885cf8c11..f01e1b61c9 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -12,7 +12,7 @@ pub struct SqlxConfig { impl Default for SqlxConfig { fn default() -> Self { SqlxConfig { - schema: "https://github.com/dawsoncodes/sqlx/sqlx-cli/sqlx-config.schema.json" + schema: "https://raw.githubusercontent.com/Dawsoncodes/sqlx/main/sqlx-cli/sqlx-config.schema.json" .to_string(), env_path: ".env".to_string(), } From 31e66c91b7b8aac500b8ef8ecf420296102fe2e6 Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 06:07:31 +0300 Subject: [PATCH 08/10] write pretty --- sqlx-cli/src/config.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index f01e1b61c9..aadd11d1f7 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::fs; +use std::fs::{self, File}; /// Main struct for the sqlx-config.json #[derive(Debug, Serialize, Deserialize)] @@ -31,9 +31,7 @@ impl SqlxConfig { // Create the file let default = SqlxConfig::default(); - let json = serde_json::to_string(&default)?; - - fs::write("sqlx-config.json", json)?; + serde_json::to_writer_pretty(File::create("sqlx-config.json")?, &default)?; Ok(default) } From 41db5bccd7448485d6c2fa7c71d2d8545d21d0d5 Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 07:06:05 +0300 Subject: [PATCH 09/10] get sqlx config from env or cmd --- sqlx-cli/sqlx-config.schema.json | 2 +- sqlx-cli/src/bin/cargo-sqlx.rs | 6 +---- sqlx-cli/src/config.rs | 46 ++++++++++++++++++++++++++++++-- sqlx-cli/src/database.rs | 15 ++++++++--- sqlx-cli/src/opt.rs | 13 +-------- 5 files changed, 58 insertions(+), 24 deletions(-) diff --git a/sqlx-cli/sqlx-config.schema.json b/sqlx-cli/sqlx-config.schema.json index de5e42a57c..c72342a4fc 100644 --- a/sqlx-cli/sqlx-config.schema.json +++ b/sqlx-cli/sqlx-config.schema.json @@ -4,7 +4,7 @@ "properties": { "env_path": { "type": "string", - "description": "Path to the .env file", + "description": "Path to the .env file, this ignores the --database-url option when the command is run. If not provided, the .env file in the current directory is used if it exists, otherwise it gets the database url from the command line option \"--database-url.\"", "default": ".env", "examples": ["../.env", "../myapp/.env"] } diff --git a/sqlx-cli/src/bin/cargo-sqlx.rs b/sqlx-cli/src/bin/cargo-sqlx.rs index dd0a6ae94e..ced8426a32 100644 --- a/sqlx-cli/src/bin/cargo-sqlx.rs +++ b/sqlx-cli/src/bin/cargo-sqlx.rs @@ -1,6 +1,6 @@ use clap::Parser; use console::style; -use sqlx_cli::{Opt, SqlxConfig}; +use sqlx_cli::Opt; use std::process; // cargo invokes this binary as `cargo-sqlx sqlx ` @@ -14,10 +14,6 @@ enum Cli { #[tokio::main] async fn main() { dotenvy::dotenv().ok(); - SqlxConfig::read() - .ok() - .ok_or(anyhow::anyhow!("Error reading sqlx-config.file")) - .unwrap(); let Cli::Sqlx(opt) = Cli::parse(); diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index aadd11d1f7..f5cc9c3286 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -1,12 +1,14 @@ use serde::{Deserialize, Serialize}; use std::fs::{self, File}; +use crate::opt::ConnectOpts; + /// Main struct for the sqlx-config.json #[derive(Debug, Serialize, Deserialize)] pub struct SqlxConfig { #[serde(rename = "$schema")] pub schema: String, - pub env_path: String, + pub env_path: Option, } impl Default for SqlxConfig { @@ -14,17 +16,29 @@ impl Default for SqlxConfig { SqlxConfig { schema: "https://raw.githubusercontent.com/Dawsoncodes/sqlx/main/sqlx-cli/sqlx-config.schema.json" .to_string(), - env_path: ".env".to_string(), + env_path: Some(".env".to_string()) } } } +// SqlxConfig::create +impl SqlxConfig { + pub fn create() -> anyhow::Result { + let default = SqlxConfig::default(); + + serde_json::to_writer_pretty(File::create("sqlx-config.json")?, &default)?; + + Ok(default) + } +} + impl SqlxConfig { pub fn read() -> anyhow::Result { let file = fs::read_to_string("sqlx-config.json"); match file { Ok(f) => { let config: SqlxConfig = serde_json::from_str(&f).unwrap(); + Ok(config) } Err(_) => { @@ -37,4 +51,32 @@ impl SqlxConfig { } } } + + pub fn get_database_url() -> Option { + let sqlx_config = SqlxConfig::read().unwrap_or_default(); + + if let Some(env_path) = sqlx_config.env_path { + dotenvy::from_filename(env_path).ok(); + + let url = dotenvy::var("DATABASE_URL").ok(); + + if let Some(url) = url { + return Some(url); + } + } + + None + } +} + +/// If the `DATABASE_URL` was available in the `env_path` specified in the `sqlx-config.json` file, +/// then it will be returned, otherwise it will check the command line option `--database-url`. +pub fn get_database_url(connect_opts: &mut ConnectOpts) -> String { + let db_url_from_config = SqlxConfig::get_database_url(); + + if let Some(db_url_from_config) = db_url_from_config { + db_url_from_config + } else { + connect_opts.required_db_url().unwrap().to_string() + } } diff --git a/sqlx-cli/src/database.rs b/sqlx-cli/src/database.rs index 702435fab9..3bedb79d9c 100644 --- a/sqlx-cli/src/database.rs +++ b/sqlx-cli/src/database.rs @@ -1,3 +1,4 @@ +use crate::config::get_database_url; use crate::migrate; use crate::opt::ConnectOpts; use console::style; @@ -17,7 +18,9 @@ pub async fn create(connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { std::sync::atomic::Ordering::Release, ); - Any::create_database(connect_opts.required_db_url()?).await?; + let database_url = get_database_url(connect_opts); + + Any::create_database(&database_url).await?; } Ok(()) @@ -28,7 +31,9 @@ pub async fn drop( confirm: bool, force: bool, ) -> anyhow::Result<()> { - if confirm && !ask_to_continue_drop(connect_opts.required_db_url()?) { + let database_url = get_database_url(connect_opts); + + if confirm && !ask_to_continue_drop(&database_url) { return Ok(()); } @@ -37,10 +42,12 @@ pub async fn drop( let exists = crate::retry_connect_errors(connect_opts, Any::database_exists).await?; if exists { + let database_url = get_database_url(connect_opts); + if force { - Any::force_drop_database(connect_opts.required_db_url()?).await?; + Any::force_drop_database(&database_url).await?; } else { - Any::drop_database(connect_opts.required_db_url()?).await?; + Any::drop_database(&database_url).await?; } } diff --git a/sqlx-cli/src/opt.rs b/sqlx-cli/src/opt.rs index 16a3e8bbf8..09a3079a52 100644 --- a/sqlx-cli/src/opt.rs +++ b/sqlx-cli/src/opt.rs @@ -246,9 +246,6 @@ pub struct ConnectOpts { #[clap(long, default_value = "10")] pub connect_timeout: u64, - #[clap(long, default_value = ".env")] - pub env_path: Option, - /// Set whether or not to create SQLite databases in Write-Ahead Log (WAL) mode: /// https://www.sqlite.org/wal.html /// @@ -265,15 +262,7 @@ pub struct ConnectOpts { impl ConnectOpts { /// Require a database URL to be provided, otherwise /// return an error. - pub fn required_db_url(&mut self) -> anyhow::Result<&str> { - if self.env_path.is_some() { - dotenvy::from_filename(&self.env_path.as_deref().unwrap()).ok(); - - let url = dotenvy::var("DATABASE_URL").ok(); - - self.database_url = url; - } - + pub fn required_db_url(&self) -> anyhow::Result<&str> { self.database_url.as_deref().ok_or_else( || anyhow::anyhow!( "the `--database-url` option the or `DATABASE_URL` environment variable must be provided" From 0c4211b04a04af44fa34ec67d0305621f974e46e Mon Sep 17 00:00:00 2001 From: Dawson <101001810+dawsoncodes@users.noreply.github.com> Date: 2024年2月16日 07:09:47 +0300 Subject: [PATCH 10/10] ConnectOpts no more mut --- sqlx-cli/src/config.rs | 2 +- sqlx-cli/src/database.rs | 12 ++++-------- sqlx-cli/src/lib.rs | 4 ++-- sqlx-cli/src/migrate.rs | 6 +++--- sqlx-cli/src/prepare.rs | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/sqlx-cli/src/config.rs b/sqlx-cli/src/config.rs index f5cc9c3286..e756e5272d 100644 --- a/sqlx-cli/src/config.rs +++ b/sqlx-cli/src/config.rs @@ -71,7 +71,7 @@ impl SqlxConfig { /// If the `DATABASE_URL` was available in the `env_path` specified in the `sqlx-config.json` file, /// then it will be returned, otherwise it will check the command line option `--database-url`. -pub fn get_database_url(connect_opts: &mut ConnectOpts) -> String { +pub fn get_database_url(connect_opts: &ConnectOpts) -> String { let db_url_from_config = SqlxConfig::get_database_url(); if let Some(db_url_from_config) = db_url_from_config { diff --git a/sqlx-cli/src/database.rs b/sqlx-cli/src/database.rs index 3bedb79d9c..96a9eb661b 100644 --- a/sqlx-cli/src/database.rs +++ b/sqlx-cli/src/database.rs @@ -6,7 +6,7 @@ use promptly::{prompt, ReadlineError}; use sqlx::any::Any; use sqlx::migrate::MigrateDatabase; -pub async fn create(connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { +pub async fn create(connect_opts: &ConnectOpts) -> anyhow::Result<()> { // NOTE: only retry the idempotent action. // We're assuming that if this succeeds, then any following operations should also succeed. let exists = crate::retry_connect_errors(connect_opts, Any::database_exists).await?; @@ -26,11 +26,7 @@ pub async fn create(connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { Ok(()) } -pub async fn drop( - connect_opts: &mut ConnectOpts, - confirm: bool, - force: bool, -) -> anyhow::Result<()> { +pub async fn drop(connect_opts: &ConnectOpts, confirm: bool, force: bool) -> anyhow::Result<()> { let database_url = get_database_url(connect_opts); if confirm && !ask_to_continue_drop(&database_url) { @@ -56,7 +52,7 @@ pub async fn drop( pub async fn reset( migration_source: &str, - connect_opts: &mut ConnectOpts, + connect_opts: &ConnectOpts, confirm: bool, force: bool, ) -> anyhow::Result<()> { @@ -64,7 +60,7 @@ pub async fn reset( setup(migration_source, connect_opts).await } -pub async fn setup(migration_source: &str, connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { +pub async fn setup(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> { create(connect_opts).await?; migrate::run(migration_source, connect_opts, false, false, None).await } diff --git a/sqlx-cli/src/lib.rs b/sqlx-cli/src/lib.rs index 55ea66a8d9..8700f90eb6 100644 --- a/sqlx-cli/src/lib.rs +++ b/sqlx-cli/src/lib.rs @@ -107,7 +107,7 @@ pub async fn run(opt: Opt) -> Result<()> { } /// Attempt to connect to the database server, retrying up to `ops.connect_timeout`. -async fn connect(opts: &mut ConnectOpts) -> anyhow::Result { +async fn connect(opts: &ConnectOpts) -> anyhow::Result { retry_connect_errors(opts, AnyConnection::connect).await } @@ -116,7 +116,7 @@ async fn connect(opts: &mut ConnectOpts) -> anyhow::Result { /// /// The closure is passed `&ops.database_url` for easy composition. async fn retry_connect_errors<'a, F, Fut, T>( - opts: &'a mut ConnectOpts, + opts: &'a ConnectOpts, mut connect: F, ) -> anyhow::Result where diff --git a/sqlx-cli/src/migrate.rs b/sqlx-cli/src/migrate.rs index 31c6438e03..10911091ac 100644 --- a/sqlx-cli/src/migrate.rs +++ b/sqlx-cli/src/migrate.rs @@ -180,7 +180,7 @@ fn short_checksum(checksum: &[u8]) -> String { s } -pub async fn info(migration_source: &str, connect_opts: &mut ConnectOpts) -> anyhow::Result<()> { +pub async fn info(migration_source: &str, connect_opts: &ConnectOpts) -> anyhow::Result<()> { let migrator = Migrator::new(Path::new(migration_source)).await?; let mut conn = crate::connect(connect_opts).await?; @@ -261,7 +261,7 @@ fn validate_applied_migrations( pub async fn run( migration_source: &str, - connect_opts: &mut ConnectOpts, + connect_opts: &ConnectOpts, dry_run: bool, ignore_missing: bool, target_version: Option, @@ -356,7 +356,7 @@ pub async fn run( pub async fn revert( migration_source: &str, - connect_opts: &mut ConnectOpts, + connect_opts: &ConnectOpts, dry_run: bool, ignore_missing: bool, target_version: Option, diff --git a/sqlx-cli/src/prepare.rs b/sqlx-cli/src/prepare.rs index db655fbc80..0cd0311f71 100644 --- a/sqlx-cli/src/prepare.rs +++ b/sqlx-cli/src/prepare.rs @@ -348,7 +348,7 @@ fn load_json_file(path: impl AsRef) -> anyhow::Result { Ok(serde_json::from_slice(&file_bytes)?) } -async fn check_backend(opts: &mut ConnectOpts) -> anyhow::Result<()> { +async fn check_backend(opts: &ConnectOpts) -> anyhow::Result<()> { crate::connect(opts).await?.close().await?; Ok(()) }

AltStyle によって変換されたページ (->オリジナル) /