From 93bb9cceb06ec818ef23f3ee16a9cb7df98eaf5d Mon Sep 17 00:00:00 2001 From: Ryan Leckey Date: Mon, 28 Dec 2020 02:18:05 -0800 Subject: [PATCH] feat(mysql): add initial Connection and Database types, hookup runtimes --- Cargo.lock | 9 ++++ Cargo.toml | 1 + sqlx-core/src/blocking/connection.rs | 3 +- sqlx-core/src/blocking/options.rs | 11 +++-- sqlx-core/src/connection.rs | 4 +- sqlx-core/src/database.rs | 4 +- sqlx-core/src/error.rs | 9 ++++ sqlx-core/src/lib.rs | 4 +- sqlx-core/src/options.rs | 4 +- sqlx-core/src/runtime.rs | 4 +- sqlx-mysql/Cargo.toml | 31 ++++++++++++++ sqlx-mysql/src/async.rs | 2 + sqlx-mysql/src/async/connection.rs | 27 ++++++++++++ sqlx-mysql/src/async/options.rs | 23 ++++++++++ sqlx-mysql/src/blocking.rs | 2 + sqlx-mysql/src/blocking/connection.rs | 19 +++++++++ sqlx-mysql/src/blocking/options.rs | 17 ++++++++ sqlx-mysql/src/connection.rs | 19 +++++++++ sqlx-mysql/src/database.rs | 15 +++++++ sqlx-mysql/src/lib.rs | 34 +++++++++++++++ sqlx-mysql/src/options.rs | 60 +++++++++++++++++++++++++++ sqlx/Cargo.toml | 12 ++++-- sqlx/src/lib.rs | 3 ++ 23 files changed, 298 insertions(+), 19 deletions(-) create mode 100644 sqlx-mysql/Cargo.toml create mode 100644 sqlx-mysql/src/async.rs create mode 100644 sqlx-mysql/src/async/connection.rs create mode 100644 sqlx-mysql/src/async/options.rs create mode 100644 sqlx-mysql/src/blocking.rs create mode 100644 sqlx-mysql/src/blocking/connection.rs create mode 100644 sqlx-mysql/src/blocking/options.rs create mode 100644 sqlx-mysql/src/connection.rs create mode 100644 sqlx-mysql/src/database.rs create mode 100644 sqlx-mysql/src/lib.rs create mode 100644 sqlx-mysql/src/options.rs diff --git a/Cargo.lock b/Cargo.lock index 70ca8f73..91503e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -759,6 +759,7 @@ name = "sqlx" version = "0.6.0-pre" dependencies = [ "sqlx-core", + "sqlx-mysql", ] [[package]] @@ -773,6 +774,14 @@ dependencies = [ "url", ] +[[package]] +name = "sqlx-mysql" +version = "0.6.0-pre" +dependencies = [ + "futures-util", + "sqlx-core", +] + [[package]] name = "syn" version = "1.0.56" diff --git a/Cargo.toml b/Cargo.toml index a6000283..c7e3334c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,6 @@ default-members = ["sqlx"] members = [ "sqlx-core", + "sqlx-mysql", "sqlx", ] diff --git a/sqlx-core/src/blocking/connection.rs b/sqlx-core/src/blocking/connection.rs index c7f21f9d..adc3c6d8 100644 --- a/sqlx-core/src/blocking/connection.rs +++ b/sqlx-core/src/blocking/connection.rs @@ -1,11 +1,12 @@ use super::{ConnectOptions, Runtime}; +use crate::DefaultRuntime; /// A unique connection (session) with a specific database. /// /// For detailed information, refer to the asynchronous version of /// this: [`Connection`][crate::Connection]. /// -pub trait Connection: crate::Connection +pub trait Connection: crate::Connection where Rt: Runtime, { diff --git a/sqlx-core/src/blocking/options.rs b/sqlx-core/src/blocking/options.rs index 67f8ca1d..d6f0fa74 100644 --- a/sqlx-core/src/blocking/options.rs +++ b/sqlx-core/src/blocking/options.rs @@ -1,20 +1,23 @@ -use super::Runtime; +use super::{Connection, Runtime}; +use crate::DefaultRuntime; /// Options which can be used to configure how a SQL connection is opened. /// /// For detailed information, refer to the asynchronous version of /// this: [`ConnectOptions`][crate::ConnectOptions]. /// -pub trait ConnectOptions: crate::ConnectOptions +pub trait ConnectOptions: crate::ConnectOptions where Rt: Runtime, { + type Connection: Connection + ?Sized; + /// Establish a connection to the database. /// /// For detailed information, refer to the asynchronous version of /// this: [`connect()`][crate::ConnectOptions::connect]. /// - fn connect(&self) -> crate::Result + fn connect(&self) -> crate::Result<>::Connection> where - Self::Connection: Sized; + >::Connection: Sized; } diff --git a/sqlx-core/src/connection.rs b/sqlx-core/src/connection.rs index 80ab99f4..e1a769e7 100644 --- a/sqlx-core/src/connection.rs +++ b/sqlx-core/src/connection.rs @@ -1,4 +1,4 @@ -use crate::{ConnectOptions, Database, Runtime}; +use crate::{ConnectOptions, Database, DefaultRuntime, Runtime}; #[cfg(feature = "async")] use futures_util::future::BoxFuture; @@ -11,7 +11,7 @@ use futures_util::future::BoxFuture; /// SQL statements will be executed and results returned within the context /// of this single SQL connection. /// -pub trait Connection: 'static + Send +pub trait Connection: 'static + Send where Rt: Runtime, { diff --git a/sqlx-core/src/database.rs b/sqlx-core/src/database.rs index 2d86d22c..027686ed 100644 --- a/sqlx-core/src/database.rs +++ b/sqlx-core/src/database.rs @@ -1,6 +1,6 @@ use std::fmt::Debug; -use crate::{Connection, Runtime}; +use crate::{Connection, DefaultRuntime, Runtime}; /// A database driver. /// @@ -8,7 +8,7 @@ use crate::{Connection, Runtime}; /// specific database (e.g., MySQL, PostgreSQL). /// // 'x: execution -pub trait Database: 'static + Sized + Debug + for<'x> HasOutput<'x> +pub trait Database: 'static + Sized + Debug + for<'x> HasOutput<'x> where Rt: Runtime, { diff --git a/sqlx-core/src/error.rs b/sqlx-core/src/error.rs index b23c91b4..e33e65ba 100644 --- a/sqlx-core/src/error.rs +++ b/sqlx-core/src/error.rs @@ -7,12 +7,14 @@ pub type Result = std::result::Result; #[non_exhaustive] pub enum Error { InvalidConnectionUrl(url::ParseError), + Network(std::io::Error), } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Self::InvalidConnectionUrl(source) => write!(f, "invalid connection url: {}", source), + Self::Network(source) => write!(f, "network: {}", source), } } } @@ -21,6 +23,13 @@ impl StdError for Error { fn source(&self) -> Option<&(dyn StdError + 'static)> { match self { Self::InvalidConnectionUrl(source) => Some(source), + Self::Network(source) => Some(source), } } } + +impl From for Error { + fn from(error: std::io::Error) -> Self { + Error::Network(error) + } +} diff --git a/sqlx-core/src/lib.rs b/sqlx-core/src/lib.rs index d4c31c5b..a732f63d 100644 --- a/sqlx-core/src/lib.rs +++ b/sqlx-core/src/lib.rs @@ -37,7 +37,7 @@ mod runtime; pub mod blocking; pub use connection::Connection; -pub use database::Database; +pub use database::{Database, HasOutput}; pub use error::{Error, Result}; pub use options::ConnectOptions; pub use runtime::Runtime; @@ -52,7 +52,7 @@ pub use runtime::Tokio; pub use runtime::Actix; #[cfg(feature = "async")] -pub(crate) use runtime::Async; +pub use runtime::Async; #[cfg(feature = "async-std")] pub type DefaultRuntime = AsyncStd; diff --git a/sqlx-core/src/options.rs b/sqlx-core/src/options.rs index 0741408c..1a8cc4a2 100644 --- a/sqlx-core/src/options.rs +++ b/sqlx-core/src/options.rs @@ -1,10 +1,10 @@ use std::fmt::Debug; use std::str::FromStr; -use crate::{Connection, Runtime}; +use crate::{Connection, DefaultRuntime, Runtime}; /// Options which can be used to configure how a SQL connection is opened. -pub trait ConnectOptions: +pub trait ConnectOptions: 'static + Send + Sync + Default + Debug + Clone + FromStr where Rt: Runtime, diff --git a/sqlx-core/src/runtime.rs b/sqlx-core/src/runtime.rs index d50ab0f5..ec9c2bb5 100644 --- a/sqlx-core/src/runtime.rs +++ b/sqlx-core/src/runtime.rs @@ -18,8 +18,8 @@ pub use self::actix::Actix; /// Describes a set of types and functions used to open and manage /// resources within SQLx. -pub trait Runtime { - type TcpStream; +pub trait Runtime: 'static + Send + Sync { + type TcpStream: Send; /// Opens a TCP connection to a remote host at the specified port. #[cfg(feature = "async")] diff --git a/sqlx-mysql/Cargo.toml b/sqlx-mysql/Cargo.toml new file mode 100644 index 00000000..32db93f6 --- /dev/null +++ b/sqlx-mysql/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "sqlx-mysql" +version = "0.6.0-pre" +repository = "https://github.com/launchbadge/sqlx" +description = "MySQL database driver for SQLx, the Rust SQL Toolkit." +license = "MIT OR Apache-2.0" +edition = "2018" +keywords = ["mysql", "sqlx", "database"] +categories = ["database", "asynchronous"] +authors = [ + "LaunchBadge " +] + +[package.metadata.docs.rs] +# > RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --all-features --no-deps --open +all-features = true +rustdoc-args = ["--cfg", "doc_cfg"] + +[features] +default = [] + +# blocking (std) runtime +blocking = ["sqlx-core/blocking"] + +# async runtime +# not meant to be used directly +async = ["futures-util", "sqlx-core/async"] + +[dependencies] +sqlx-core = { version = "0.6.0-pre", path = "../sqlx-core" } +futures-util = { version = "0.3.8", optional = true } diff --git a/sqlx-mysql/src/async.rs b/sqlx-mysql/src/async.rs new file mode 100644 index 00000000..217280ea --- /dev/null +++ b/sqlx-mysql/src/async.rs @@ -0,0 +1,2 @@ +mod connection; +mod options; diff --git a/sqlx-mysql/src/async/connection.rs b/sqlx-mysql/src/async/connection.rs new file mode 100644 index 00000000..5ca83607 --- /dev/null +++ b/sqlx-mysql/src/async/connection.rs @@ -0,0 +1,27 @@ +use futures_util::future::BoxFuture; +use sqlx_core::{Async, Connection, Result, Runtime}; + +use crate::{MySql, MySqlConnectOptions, MySqlConnection}; + +impl Connection for MySqlConnection +where + Rt: Runtime, +{ + type Database = MySql; + + type Options = MySqlConnectOptions; + + fn close(self) -> BoxFuture<'static, Result<()>> + where + Rt: Async, + { + unimplemented!() + } + + fn ping(&mut self) -> BoxFuture<'_, Result<()>> + where + Rt: Async, + { + unimplemented!() + } +} diff --git a/sqlx-mysql/src/async/options.rs b/sqlx-mysql/src/async/options.rs new file mode 100644 index 00000000..bddded69 --- /dev/null +++ b/sqlx-mysql/src/async/options.rs @@ -0,0 +1,23 @@ +use futures_util::{future::BoxFuture, FutureExt}; +use sqlx_core::{Async, ConnectOptions, Result, Runtime}; + +use crate::{MySqlConnectOptions, MySqlConnection}; + +impl ConnectOptions for MySqlConnectOptions +where + Rt: Runtime, +{ + type Connection = MySqlConnection; + + fn connect(&self) -> BoxFuture<'_, Result> + where + Self::Connection: Sized, + Rt: Async, + { + FutureExt::boxed(async move { + let stream = Rt::connect_tcp(&self.host, self.port).await?; + + Ok(MySqlConnection { stream }) + }) + } +} diff --git a/sqlx-mysql/src/blocking.rs b/sqlx-mysql/src/blocking.rs new file mode 100644 index 00000000..217280ea --- /dev/null +++ b/sqlx-mysql/src/blocking.rs @@ -0,0 +1,2 @@ +mod connection; +mod options; diff --git a/sqlx-mysql/src/blocking/connection.rs b/sqlx-mysql/src/blocking/connection.rs new file mode 100644 index 00000000..2b486898 --- /dev/null +++ b/sqlx-mysql/src/blocking/connection.rs @@ -0,0 +1,19 @@ +use sqlx_core::blocking::{Connection, Runtime}; +use sqlx_core::Result; + +use crate::{MySqlConnectOptions, MySqlConnection}; + +impl Connection for MySqlConnection +where + Rt: Runtime, +{ + type Options = MySqlConnectOptions; + + fn close(self) -> Result<()> { + unimplemented!() + } + + fn ping(&mut self) -> Result<()> { + unimplemented!() + } +} diff --git a/sqlx-mysql/src/blocking/options.rs b/sqlx-mysql/src/blocking/options.rs new file mode 100644 index 00000000..db19e587 --- /dev/null +++ b/sqlx-mysql/src/blocking/options.rs @@ -0,0 +1,17 @@ +use sqlx_core::blocking::{ConnectOptions, Runtime}; +use sqlx_core::Result; + +use crate::{MySqlConnectOptions, MySqlConnection}; + +impl ConnectOptions for MySqlConnectOptions +where + Rt: Runtime, +{ + type Connection = MySqlConnection; + + fn connect(&self) -> Result> { + let stream = ::connect_tcp(&self.host, self.port)?; + + Ok(MySqlConnection { stream }) + } +} diff --git a/sqlx-mysql/src/connection.rs b/sqlx-mysql/src/connection.rs new file mode 100644 index 00000000..e795766f --- /dev/null +++ b/sqlx-mysql/src/connection.rs @@ -0,0 +1,19 @@ +use std::fmt::{self, Debug, Formatter}; + +use sqlx_core::{DefaultRuntime, Runtime}; + +pub struct MySqlConnection +where + Rt: Runtime, +{ + pub(crate) stream: Rt::TcpStream, +} + +impl Debug for MySqlConnection +where + Rt: Runtime, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("MySqlConnection").finish() + } +} diff --git a/sqlx-mysql/src/database.rs b/sqlx-mysql/src/database.rs new file mode 100644 index 00000000..26090753 --- /dev/null +++ b/sqlx-mysql/src/database.rs @@ -0,0 +1,15 @@ +use sqlx_core::{Database, HasOutput, Runtime}; + +#[derive(Debug)] +pub struct MySql; + +impl Database for MySql +where + Rt: Runtime, +{ + type Connection = super::MySqlConnection; +} + +impl<'x> HasOutput<'x> for MySql { + type Output = &'x mut Vec; +} diff --git a/sqlx-mysql/src/lib.rs b/sqlx-mysql/src/lib.rs new file mode 100644 index 00000000..075a0d85 --- /dev/null +++ b/sqlx-mysql/src/lib.rs @@ -0,0 +1,34 @@ +//! [MySQL] database driver for [SQLx][sqlx_core], the Rust SQL toolkit. +//! +//! [MySQL]: https://www.mysql.com/ +//! +#![cfg_attr(doc_cfg, feature(doc_cfg))] +#![deny(unsafe_code)] +#![warn(rust_2018_idioms)] +#![warn(future_incompatible)] +#![warn(clippy::pedantic)] +#![warn(clippy::cargo_common_metadata)] +#![warn(clippy::multiple_crate_versions)] +#![warn(clippy::cognitive_complexity)] +#![warn(clippy::future_not_send)] +#![warn(clippy::missing_const_for_fn)] +#![warn(clippy::needless_borrow)] +#![warn(clippy::redundant_pub_crate)] +#![warn(clippy::string_lit_as_bytes)] +#![warn(clippy::use_self)] +#![warn(clippy::useless_let_if_seq)] +#![allow(clippy::doc_markdown)] + +mod connection; +mod database; +mod options; + +#[cfg(feature = "blocking")] +mod blocking; + +#[cfg(feature = "async")] +mod r#async; + +pub use connection::MySqlConnection; +pub use database::MySql; +pub use options::MySqlConnectOptions; diff --git a/sqlx-mysql/src/options.rs b/sqlx-mysql/src/options.rs new file mode 100644 index 00000000..c1db8f41 --- /dev/null +++ b/sqlx-mysql/src/options.rs @@ -0,0 +1,60 @@ +use std::fmt::{self, Debug, Formatter}; +use std::marker::PhantomData; +use std::str::FromStr; + +use sqlx_core::{DefaultRuntime, Runtime}; + +pub struct MySqlConnectOptions +where + Rt: Runtime, +{ + runtime: PhantomData, + pub(crate) host: String, + pub(crate) port: u16, +} + +impl Default for MySqlConnectOptions +where + Rt: Runtime, +{ + fn default() -> Self { + Self { + host: "localhost".to_owned(), + runtime: PhantomData, + port: 3306, + } + } +} + +impl Clone for MySqlConnectOptions +where + Rt: Runtime, +{ + fn clone(&self) -> Self { + unimplemented!() + } +} + +impl Debug for MySqlConnectOptions +where + Rt: Runtime, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("MySqlConnectOptions").finish() + } +} + +impl FromStr for MySqlConnectOptions +where + Rt: Runtime, +{ + type Err = sqlx_core::Error; + + fn from_str(s: &str) -> Result { + Ok(Self { + host: "localhost".to_owned(), + runtime: PhantomData, + port: 3306, + }) + } +} diff --git a/sqlx/Cargo.toml b/sqlx/Cargo.toml index 76b8ce1f..3ac72297 100644 --- a/sqlx/Cargo.toml +++ b/sqlx/Cargo.toml @@ -16,12 +16,16 @@ authors = [ default = [] # blocking (std) runtime -blocking = ["sqlx-core/blocking"] +blocking = ["sqlx-core/blocking", "sqlx-mysql/blocking"] # async runtimes -async-std = ["sqlx-core/async-std"] -actix = ["sqlx-core/actix"] -tokio = ["sqlx-core/tokio"] +async-std = ["sqlx-core/async-std", "sqlx-mysql/async"] +actix = ["sqlx-core/actix", "sqlx-mysql/async"] +tokio = ["sqlx-core/tokio", "sqlx-mysql/async"] + +# databases +mysql = ["sqlx-mysql"] [dependencies] sqlx-core = { version = "0.6.0-pre", path = "../sqlx-core" } +sqlx-mysql = { version = "0.6.0-pre", path = "../sqlx-mysql", optional = true } diff --git a/sqlx/src/lib.rs b/sqlx/src/lib.rs index 716c1550..1ca18446 100644 --- a/sqlx/src/lib.rs +++ b/sqlx/src/lib.rs @@ -31,3 +31,6 @@ pub use sqlx_core::Tokio; #[cfg(feature = "actix")] pub use sqlx_core::Actix; + +#[cfg(feature = "mysql")] +pub use sqlx_mysql as mysql;