use std::sync::Arc;
use url::Url;
use crate::buffer_pool::{BufferPool, GLOBAL_BUFFER_POOL};
use crate::constant::CapabilityFlags;
use crate::error::Error;
#[derive(Debug, Clone)]
pub struct Opts {
pub tcp_nodelay: bool,
pub capabilities: CapabilityFlags,
pub compress: bool,
pub db: Option<String>,
pub host: String,
pub port: u16,
pub socket: Option<String>,
pub user: String,
pub password: String,
pub tls: bool,
pub upgrade_to_unix_socket: bool,
pub init_command: Option<String>,
pub pool_reset_conn: bool,
pub pool_max_idle_conn: usize,
pub pool_max_concurrency: Option<usize>,
pub buffer_pool: Arc<BufferPool>,
}
impl Default for Opts {
fn default() -> Self {
Self {
tcp_nodelay: true,
capabilities: CapabilityFlags::empty(),
compress: false,
db: None,
host: String::new(),
port: 3306,
socket: None,
user: String::new(),
password: String::new(),
tls: false,
upgrade_to_unix_socket: true,
init_command: None,
pool_reset_conn: true,
pool_max_idle_conn: 100,
pool_max_concurrency: None,
buffer_pool: Arc::clone(&GLOBAL_BUFFER_POOL),
}
}
}
fn parse_bool(key: &str, value: &str) -> Result<bool, Error> {
match value {
"1" | "true" | "True" => Ok(true),
"0" | "false" | "False" => Ok(false),
_ => Err(Error::BadUsageError(format!(
"Invalid boolean value '{}' for parameter '{}', expected 1, 0, true, false, True, or False",
value, key
))),
}
}
fn parse_usize(key: &str, value: &str) -> Result<usize, Error> {
value.parse().map_err(|_unhelpful_err| {
Error::BadUsageError(format!(
"Invalid unsigned integer value '{}' for parameter '{}'",
value, key
))
})
}
impl TryFrom<&Url> for Opts {
type Error = Error;
fn try_from(url: &Url) -> Result<Self, Self::Error> {
if url.scheme() != "mysql" {
return Err(Error::BadUsageError(format!(
"Invalid URL scheme '{}', expected 'mysql'",
url.scheme()
)));
}
let host = url.host_str().unwrap_or_default().to_string();
let port = url.port().unwrap_or(3306);
let user = url.username().to_string();
let password = url.password().unwrap_or_default().to_string();
let db = url
.path()
.strip_prefix('/')
.filter(|db| !db.is_empty())
.map(ToString::to_string);
let mut opts = Self {
host,
port,
user,
password,
db,
..Default::default()
};
for (key, value) in url.query_pairs() {
match key.as_ref() {
"socket" => opts.socket = Some(value.into_owned()),
"tls" | "ssl" => opts.tls = parse_bool(&key, &value)?,
"compress" => opts.compress = parse_bool(&key, &value)?,
"tcp_nodelay" => opts.tcp_nodelay = parse_bool(&key, &value)?,
"upgrade_to_unix_socket" => opts.upgrade_to_unix_socket = parse_bool(&key, &value)?,
"init_command" => opts.init_command = Some(value.into_owned()),
"pool_reset_conn" => opts.pool_reset_conn = parse_bool(&key, &value)?,
"pool_max_idle_conn" => opts.pool_max_idle_conn = parse_usize(&key, &value)?,
"pool_max_concurrency" => {
opts.pool_max_concurrency = Some(parse_usize(&key, &value)?)
}
_ => {
return Err(Error::BadUsageError(format!(
"Unknown query parameter '{}'",
key
)));
}
}
}
Ok(opts)
}
}
impl TryFrom<&str> for Opts {
type Error = Error;
fn try_from(url: &str) -> Result<Self, Self::Error> {
let parsed = Url::parse(url)
.map_err(|e| Error::BadUsageError(format!("Failed to parse MySQL URL: {}", e)))?;
Self::try_from(&parsed)
}
}