use std::str::FromStr;
use rsfbclient::charset::Charset;
use rsfbclient::{Dialect, FbError};
use sqlx_core::error::Error;
use sqlx_core::percent_encoding::percent_decode_str;
use sqlx_core::Url;
use super::FbConnectOptions;
impl FbConnectOptions {
pub(crate) fn parse_from_url(url: &Url) -> Result<Self, Error> {
let settings = parse(url).map_err(Error::config)?;
let mut options = FbConnectOptions::new();
if let Some(user) = settings.user {
options = options.username(user);
}
if let Some(pass) = settings.pass {
options = options.password(pass);
}
if let Some(host) = settings.host {
options = options.host(host);
}
if let Some(port) = settings.port {
options = options.port(port);
}
options = options.database(settings.db_name);
if let Some(charset) = settings.charset {
options = options.charset(charset);
}
if let Some(dialect) = settings.dialect {
options = options.dialect(dialect);
}
if let Some(stmt_cache_size) = settings.stmt_cache_size {
options = options.stmt_cache_size(stmt_cache_size);
}
if let Some(role_name) = settings.role_name {
options = options.role_name(role_name);
}
Ok(options)
}
}
pub struct ConnStringSettings {
pub user: Option<String>,
pub pass: Option<String>,
pub host: Option<String>,
pub port: Option<u16>,
pub db_name: String,
pub charset: Option<Charset>,
pub dialect: Option<Dialect>,
pub stmt_cache_size: Option<usize>,
pub role_name: Option<String>,
}
fn parse(url: &Url) -> Result<ConnStringSettings, FbError> {
if url.scheme().to_lowercase() != "firebird" {
return Err(FbError::from(
"The string must start with the prefix 'firebird://'",
));
}
let user = match url.username() {
"" => None,
u => Some(percent_decode_str(u).decode_utf8()?.into_owned()),
};
let pass = match url.password() {
Some(p) => Some(percent_decode_str(p).decode_utf8()?.into_owned()),
_ => None,
};
let mut host = match url.host_str() {
Some(h) => Some(percent_decode_str(h).decode_utf8()?.into_owned()),
_ => None,
};
let port = url.port();
let mut db_name = match url.path() {
"" => None,
db => {
let db = percent_decode_str(db).decode_utf8()?.into_owned();
if db.starts_with('/') && url.has_host() {
Some(db.replacen('/', "", 1))
} else {
Some(db)
}
},
};
match (&host, &db_name) {
(Some(h), Some(db)) => {
if h.len() == 1 && user.is_none() && pass.is_none() && port.is_none() {
db_name = Some(format!("{}:/{}", h, db));
host = None;
}
},
(Some(h), None) => {
if user.is_none() && pass.is_none() && port.is_none() {
db_name = Some(h.to_string());
host = None;
}
},
_ => {},
}
let db_name = db_name.ok_or_else(|| FbError::from("The database name/path is required"))?;
let mut dialect = None;
let mut charset = None;
let mut stmt_cache_size = None;
let mut role_name = None;
for (param, val) in url.query_pairs() {
match param.to_string().as_str() {
"dialect" => {
dialect = match Dialect::from_str(&val) {
Ok(d) => Some(d),
_ => None,
};
},
"charset" => {
charset = match Charset::from_str(&val) {
Ok(d) => Some(d),
_ => None,
};
},
"stmt_cache_size" => {
stmt_cache_size = match val.parse::<usize>() {
Ok(v) => Some(v),
_ => None,
};
},
"role_name" => {
if val != "" {
role_name = Some(val.to_string());
}
},
_ => {},
}
}
Ok(ConnStringSettings {
user,
pass,
host,
port,
db_name,
charset,
dialect,
stmt_cache_size,
role_name,
})
}
impl FromStr for FbConnectOptions {
type Err = Error;
fn from_str(url: &str) -> Result<Self, Self::Err> {
let url = Url::parse(url).map_err(Error::config)?;
Self::parse_from_url(&url)
}
}