style: clippy

This commit is contained in:
Ryan Leckey 2021-01-08 16:39:12 -08:00
parent 5e7f35e28b
commit 16077216df
No known key found for this signature in database
GPG Key ID: F8AA68C235AB08C9
23 changed files with 146 additions and 64 deletions

View File

@ -11,8 +11,8 @@ authors = [
actix-web = "3.3.2"
anyhow = "1.0.36"
async-std = { version = "1.8.0", features = ["attributes"] }
#sqlx = { path = "../../sqlx", features = ["tokio", "mysql", "blocking", "async-std", "actix"] }
sqlx = { path = "../../sqlx", features = ["tokio", "mysql"] }
tokio = { version = "1.0.1", features = ["rt", "rt-multi-thread", "macros"] }
log = "0.4"
env_logger = "0.8.2"
#sqlx = { path = "../../sqlx", features = ["tokio", "mysql"] }
sqlx = { path = "../../sqlx", features = ["blocking", "mysql"] }

View File

@ -1,9 +1,19 @@
use sqlx::mysql::MySqlConnection;
use sqlx::prelude::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let _conn = <MySqlConnection>::connect("mysql://root:password@localhost:3307").await?;
// #[tokio::main]
// async fn main() -> anyhow::Result<()> {
// env_logger::try_init()?;
//
// let _conn = <MySqlConnection>::connect("mysql://root:password@localhost").await?;
//
// Ok(())
// }
fn main() -> anyhow::Result<()> {
env_logger::try_init()?;
let _conn = <MySqlConnection>::connect("mysql://root:password@localhost")?;
Ok(())
}

View File

@ -46,6 +46,7 @@ where
/// ```
///
#[cfg(feature = "async")]
#[must_use]
fn connect(url: &str) -> BoxFuture<'_, crate::Result<Self>>
where
Self: Sized,

View File

@ -4,14 +4,15 @@ use bytes::{Buf, Bytes};
use bytestring::ByteString;
use memchr::memchr;
// UNSAFE: _unchecked string methods
// intended for use when the protocol is *known* to always produce
// valid UTF-8 data
#[allow(clippy::module_name_repetitions)]
pub trait BufExt: Buf {
/// # Safety
/// This function is unsafe because it does not check the bytes that are read are valid UTF-8.
#[allow(unsafe_code)]
unsafe fn get_str_unchecked(&mut self, n: usize) -> ByteString;
/// # Safety
/// This function is unsafe because it does not check the bytes that are read are valid UTF-8.
#[allow(unsafe_code)]
unsafe fn get_str_nul_unchecked(&mut self) -> io::Result<ByteString>;
}

View File

@ -123,7 +123,7 @@ macro_rules! read {
#[cfg(feature = "async")]
impl<S> BufStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
S: AsyncRead + AsyncWrite + Send + Unpin,
{
pub async fn flush_async(&mut self) -> crate::Result<()> {
// write as much as we can each time and move the cursor as we write from the buffer

View File

@ -1,3 +1,4 @@
#[allow(clippy::module_name_repetitions)]
pub trait WriteExt {
fn write_str_nul(&mut self, s: &str);
fn write_maybe_str_nul(&mut self, s: Option<&str>);

View File

@ -6,13 +6,11 @@
#![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)]

View File

@ -30,6 +30,7 @@ pub trait Runtime: 'static + Send + Sync {
}
#[cfg(feature = "async")]
#[allow(clippy::module_name_repetitions)]
pub trait AsyncRuntime: Runtime {
/// Opens a TCP connection to a remote host at the specified port.
fn connect_tcp(

View File

@ -19,6 +19,7 @@ pub struct Mock;
#[derive(Debug)]
#[doc(hidden)]
#[allow(clippy::module_name_repetitions)]
pub struct MockStream {
port: u16,
rbuf: BytesMut,
@ -58,6 +59,7 @@ impl crate::blocking::Runtime for Mock {
}
impl Mock {
#[must_use]
pub fn stream() -> MockStream {
let port = MOCK_STREAM_PORT.fetch_add(1, Ordering::SeqCst) + 1;
@ -74,7 +76,8 @@ impl Mock {
}
impl MockStream {
pub fn port(&self) -> u16 {
#[must_use]
pub const fn port(&self) -> u16 {
self.port
}
}
@ -97,7 +100,8 @@ impl std::io::Read for MockStream {
}
// no bytes in the buffer, ask the channel for more
let message = self.read.recv().map_err(|_| std::io::ErrorKind::ConnectionAborted)?;
#[allow(clippy::map_err_ignore)]
let message = self.read.recv().map_err(|_err| std::io::ErrorKind::ConnectionAborted)?;
self.rbuf.extend_from_slice(&message);
// loop around and now send out this message

View File

@ -39,6 +39,7 @@ impl MySqlBufExt for Bytes {
#[allow(unsafe_code)]
unsafe fn get_str_lenenc_unchecked(&mut self) -> ByteString {
#[allow(clippy::cast_possible_truncation)]
let len = self.get_uint_lenenc() as usize;
self.get_str_unchecked(len)
@ -50,6 +51,7 @@ impl MySqlBufExt for Bytes {
}
fn get_bytes_lenenc(&mut self) -> Bytes {
#[allow(clippy::cast_possible_truncation)]
let len = self.get_uint_lenenc() as usize;
self.split_to(len)

View File

@ -13,16 +13,22 @@ impl MySqlWriteExt for Vec<u8> {
if value < 251 {
// if the value is < 251, it is stored as a 1-byte integer
#[allow(clippy::cast_possible_truncation)]
self.push(value as u8);
} else if value < 0x1_00_00 {
// if the value is ≥ 251 and < (2 ** 16), it is stored as fc + 2-byte integer
self.reserve(3);
self.push(0xfc);
#[allow(clippy::cast_possible_truncation)]
self.extend_from_slice(&(value as u16).to_le_bytes());
} else if value < 0x1_00_00_00 {
// if the value is ≥ (2 ** 16) and < (2 ** 24), it is stored as fd + 3-byte integer
self.reserve(4);
self.push(0xfd);
#[allow(clippy::cast_possible_truncation)]
self.extend_from_slice(&(value as u32).to_le_bytes()[..3]);
} else {
// if the value is ≥ (2 ** 24) and < (2 ** 64) it is stored as fe + 8-byte integer

View File

@ -8,13 +8,11 @@
#![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)]

View File

@ -26,6 +26,7 @@ mod parse;
///
/// - Only a single host is supported.
///
#[allow(clippy::module_name_repetitions)]
pub struct MySqlConnectOptions<Rt = DefaultRuntime>
where
Rt: Runtime,
@ -84,41 +85,49 @@ where
Rt: Runtime,
{
/// Returns the hostname of the database server.
#[must_use]
pub fn get_host(&self) -> &str {
self.address.as_ref().left().map(|(host, _)| &**host).unwrap_or(default::HOST)
self.address.as_ref().left().map_or(default::HOST, |(host, _)| &**host)
}
/// Returns the TCP port number of the database server.
#[must_use]
pub fn get_port(&self) -> u16 {
self.address.as_ref().left().map(|(_, port)| *port).unwrap_or(default::PORT)
self.address.as_ref().left().map_or(default::PORT, |(_, port)| *port)
}
/// Returns the path to the Unix domain socket, if one is configured.
#[must_use]
pub fn get_socket(&self) -> Option<&Path> {
self.address.as_ref().right().map(|buf| buf.as_path())
self.address.as_ref().right().map(PathBuf::as_path)
}
/// Returns the default database name.
#[must_use]
pub fn get_database(&self) -> Option<&str> {
self.database.as_deref()
}
/// Returns the username to be used for authentication.
#[must_use]
pub fn get_username(&self) -> Option<&str> {
self.username.as_deref()
}
/// Returns the password to be used for authentication.
#[must_use]
pub fn get_password(&self) -> Option<&str> {
self.password.as_deref()
}
/// Returns the character set for the connection.
#[must_use]
pub fn get_charset(&self) -> &str {
&self.charset
}
/// Returns the timezone for the connection.
#[must_use]
pub fn get_timezone(&self) -> &str {
&self.timezone
}

View File

@ -31,6 +31,7 @@ where
Rt: Runtime,
{
/// Creates a default set of options ready for configuration.
#[must_use]
pub fn new() -> Self {
Self::default()
}

View File

@ -1,6 +1,5 @@
use std::error::Error as StdError;
use std::fmt::Debug;
use std::str::FromStr;
use bytes::buf::Chain;
use bytes::Bytes;
@ -34,10 +33,8 @@ pub(crate) trait AuthPlugin: 'static + Debug + Send + Sync {
) -> Result<Option<Vec<u8>>>;
}
impl FromStr for Box<dyn AuthPlugin> {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
impl dyn AuthPlugin {
pub(crate) fn parse(s: &str) -> Result<Box<Self>> {
match s {
_ if s == CachingSha2AuthPlugin.name() => Ok(Box::new(CachingSha2AuthPlugin)),
_ if s == Sha256AuthPlugin.name() => Ok(Box::new(Sha256AuthPlugin)),
@ -68,7 +65,7 @@ fn err_msg(plugin: &'static str, message: &str) -> Error {
))
}
fn err<E>(plugin: &'static str, error: E) -> Error
fn err<E>(plugin: &'static str, error: &E) -> Error
where
E: StdError,
{

View File

@ -65,7 +65,7 @@ impl super::AuthPlugin for CachingSha2AuthPlugin {
AUTH_CONTINUE => {
// ruh roh, we need to ask for the RSA public key, so we can
// encrypt our password directly and send it
return Ok(Some(vec![0x2_u8]));
Ok(Some(vec![0x2_u8]))
}
_ => {

View File

@ -23,29 +23,29 @@ pub(crate) fn encrypt(
super::xor_eq(&mut pass, &*nonce);
// client sends an RSA encrypted password
let pkey = parse_rsa_pub_key(plugin, key)?;
let public = parse_rsa_pub_key(plugin, key)?;
let padding = PaddingScheme::new_oaep::<sha1::Sha1>();
pkey.encrypt(&mut rng(), padding, &pass[..]).map_err(|err| super::err(plugin, err))
public.encrypt(&mut rng(), padding, &pass[..]).map_err(|err| super::err(plugin, &err))
}
// https://docs.rs/rsa/0.3.0/rsa/struct.RSAPublicKey.html?search=#example-1
fn parse_rsa_pub_key(plugin: &'static str, key: &[u8]) -> Result<RSAPublicKey> {
let key = from_utf8(key).map_err(|err| super::err(plugin, err))?;
let key = from_utf8(key).map_err(|err| super::err(plugin, &err))?;
// Takes advantage of the knowledge that we know
// we are receiving a PKCS#8 RSA Public Key at all
// times from MySQL
let encoded =
key.lines().filter(|line| !line.starts_with("-")).fold(String::new(), |mut data, line| {
data.push_str(&line);
key.lines().filter(|line| !line.starts_with('-')).fold(String::new(), |mut data, line| {
data.push_str(line);
data
});
let der = base64::decode(&encoded).map_err(|err| super::err(plugin, err))?;
let der = base64::decode(&encoded).map_err(|err| super::err(plugin, &err))?;
RSAPublicKey::from_pkcs8(&der).map_err(|err| super::err(plugin, err))
RSAPublicKey::from_pkcs8(&der).map_err(|err| super::err(plugin, &err))
}
fn to_asciz(s: &str) -> Vec<u8> {

View File

@ -1,5 +1,3 @@
use std::str::FromStr;
use bytes::{buf::Chain, Buf, Bytes};
use sqlx_core::io::{BufExt, Deserialize};
use sqlx_core::Result;
@ -32,7 +30,7 @@ impl Deserialize<'_, Capabilities> for AuthSwitch {
let plugin_data = buf.chain(Bytes::new());
let plugin = <Box<dyn AuthPlugin>>::from_str(&*name)?;
let plugin = AuthPlugin::parse(&*name)?;
Ok(Self { plugin, plugin_data })
}

View File

@ -5,76 +5,76 @@ bitflags::bitflags! {
pub struct Capabilities: u64 {
// use the improved version of "old password auth"
// assumed to be set since 4.1
const LONG_PASSWORD = 0x00000001;
const LONG_PASSWORD = 0x0000_0001;
// send found (read: matched) rows instead of affected rows in the EOF packet
const FOUND_ROWS = 0x00000002;
const FOUND_ROWS = 0x0000_0002;
// longer flags for column metadata
// not used if PROTOCOL_41 is used (long flags are always received)
const LONG_FLAG = 0x00000004;
const LONG_FLAG = 0x0000_0004;
// database (schema) name can be specified on connect in Handshake Response Packet
const CONNECT_WITH_DB = 0x00000008;
const CONNECT_WITH_DB = 0x0000_0008;
// do not permit `database.table.column`
const NO_SCHEMA = 0x00000010;
const NO_SCHEMA = 0x0000_0010;
// compression protocol supported
// todo: expose in MySqlConnectOptions
const COMPRESS = 0x00000020;
const COMPRESS = 0x0000_0020;
// legacy flag to enable special ODBC handling
// no handling since MySQL v3.22
const ODBC = 0x00000040;
const ODBC = 0x0000_0040;
// enable LOAD DATA LOCAL
const LOCAL_FILES = 0x00000080;
const LOCAL_FILES = 0x0000_0080;
// SQL parser can ignore spaces before '('
const IGNORE_SPACE = 0x00000100;
const IGNORE_SPACE = 0x0000_0100;
// uses the 4.1+ protocol
const PROTOCOL_41 = 0x00000200;
const PROTOCOL_41 = 0x0000_0200;
// this is an interactive client
// wait_timeout versus wait_interactive_timeout.
const INTERACTIVE = 0x00000400;
const INTERACTIVE = 0x0000_0400;
// use SSL encryption for this session
const SSL = 0x00000800;
const SSL = 0x0000_0800;
// EOF packets will contain transaction status flags
const TRANSACTIONS = 0x00002000;
const TRANSACTIONS = 0x0000_2000;
// support native 4.1+ authentication
const SECURE_CONNECTION = 0x00008000;
const SECURE_CONNECTION = 0x0000_8000;
// can handle multiple statements in COM_QUERY and COM_STMT_PREPARE
const MULTI_STATEMENTS = 0x00010000;
const MULTI_STATEMENTS = 0x0001_0000;
// can send multiple result sets for COM_QUERY
const MULTI_RESULTS = 0x00020000;
const MULTI_RESULTS = 0x0002_0000;
// can send multiple result sets for COM_STMT_EXECUTE
const PS_MULTI_RESULTS = 0x00040000;
const PS_MULTI_RESULTS = 0x0004_0000;
// supports authentication plugins
const PLUGIN_AUTH = 0x00080000;
const PLUGIN_AUTH = 0x0008_0000;
// permits connection attributes
const CONNECT_ATTRS = 0x00100000;
const CONNECT_ATTRS = 0x0010_0000;
// enable authentication response packet to be larger than 255 bytes.
const PLUGIN_AUTH_LENENC_DATA = 0x00200000;
const PLUGIN_AUTH_LENENC_DATA = 0x0020_0000;
// can handle connection for a user account with expired passwords
const CAN_HANDLE_EXPIRED_PASSWORDS = 0x00400000;
const CAN_HANDLE_EXPIRED_PASSWORDS = 0x0040_0000;
// capable of handling server state change information in an OK packet
const SESSION_TRACK = 0x00800000;
const SESSION_TRACK = 0x0080_0000;
// client no longer needs EOF_Packet and will use OK_Packet instead.
const DEPRECATE_EOF = 0x01000000;
const DEPRECATE_EOF = 0x0100_0000;
}
}

View File

@ -1,5 +1,3 @@
use std::str::FromStr;
use bytes::buf::Chain;
use bytes::{Buf, Bytes};
use bytestring::ByteString;
@ -103,7 +101,7 @@ impl Deserialize<'_, Capabilities> for Handshake {
// read to NUL or read to the end if we can't find a NUL
let auth_plugin_name_end = memchr(b'\0', &buf).unwrap_or(buf.len());
let auth_plugin_name_end = memchr(b'\0', &buf).unwrap_or_else(|| buf.len());
// UNSAFE: auth plugin names are known to be ASCII
#[allow(unsafe_code)]
@ -123,7 +121,7 @@ impl Deserialize<'_, Capabilities> for Handshake {
status,
auth_plugin_data: auth_plugin_data_1.chain(auth_plugin_data_2),
auth_plugin: auth_plugin_name
.map(|name| <Box<dyn AuthPlugin>>::from_str(&name))
.map(|name| AuthPlugin::parse(&name))
.transpose()?
.unwrap_or_else(|| Box::new(NativeAuthPlugin)),
})

View File

@ -19,6 +19,9 @@ pub(crate) struct HandshakeResponse<'a> {
impl Serialize<'_, Capabilities> for HandshakeResponse<'_> {
fn serialize_with(&self, buf: &mut Vec<u8>, capabilities: Capabilities) -> Result<()> {
// the truncation is the intent
// capability bits over 32 are MariaDB only (and we don't currently support them)
#[allow(clippy::cast_possible_truncation)]
buf.extend_from_slice(&(capabilities.bits() as u32).to_le_bytes());
buf.extend_from_slice(&self.max_packet_size.to_le_bytes());
buf.push(self.charset);
@ -36,7 +39,11 @@ impl Serialize<'_, Capabilities> for HandshakeResponse<'_> {
debug_assert!(auth_response.len() <= u8::max_value().into());
buf.reserve(1 + auth_response.len());
// in debug mode, we assert that the auth response is not too big
#[allow(clippy::cast_possible_truncation)]
buf.push(auth_response.len() as u8);
buf.extend_from_slice(auth_response);
} else {
buf.reserve(1 + auth_response.len());

View File

@ -4,7 +4,6 @@
#![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)]

View File

@ -69,6 +69,53 @@ def run_check(project_name: str):
], cwd=project_dir, comment=f"check {project} +async,+blocking")
def run_clippy(project_name: str):
project = f"sqlx-{project_name}"
tag = f"clippy:{project_name}"
if argv.target is not None and argv.target not in tag:
return
if argv.list_targets:
print(tag)
return
# update timestamp so clippy will run
Path(f"{project}/src/lib.rs").touch()
run([
"cargo", "clippy",
"--manifest-path", f"{project}/Cargo.toml",
], cwd=project_dir, comment=f"clippy {project}")
# update timestamp so clippy will run
Path(f"{project}/src/lib.rs").touch()
run([
"cargo", "clippy",
"--manifest-path", f"{project}/Cargo.toml",
"--features", "blocking",
], cwd=project_dir, comment=f"clippy {project} +blocking")
# update timestamp so clippy will run
Path(f"{project}/src/lib.rs").touch()
run([
"cargo", "clippy",
"--manifest-path", f"{project}/Cargo.toml",
"--features", "async",
], cwd=project_dir, comment=f"clippy {project} +async")
# update timestamp so clippy will run
Path(f"{project}/src/lib.rs").touch()
run([
"cargo", "clippy",
"--manifest-path", f"{project}/Cargo.toml",
"--features", "blocking,async",
], cwd=project_dir, comment=f"clippy {project} +async,+blocking")
def run_unit_test(project_name: str):
project = f"sqlx-{project_name}"
tag = f"unit:{project_name}"
@ -121,6 +168,10 @@ def main():
run_check("core")
run_check("mysql")
# run checks
run_clippy("core")
run_clippy("mysql")
# run unit tests, collect test binary filenames
run_unit_test("core")
run_unit_test("mysql")