use std::env;
use std::str::FromStr;
use std::time::Duration;
use api::auth::{Authentication, Credentials};
use api::types::Url;
pub const DEFAULT_PROTOCOL: &str = "http";
pub const DEFAULT_HOST: &str = "localhost";
pub const DEFAULT_PORT: u16 = 8529;
pub const DEFAULT_USERNAME: &str = "root";
pub const DEFAULT_PASSWORD: &str = "";
pub const DEFAULT_DATABASE_NAME: &str = "_system";
pub const DEFAULT_TIMEOUT: u64 = 30;
pub const ENV_ROOT_PASSWORD: &str = "ARANGO_ROOT_PASSWORD";
#[derive(Clone, PartialEq, Eq, Debug, Fail)]
pub enum Error {
#[fail(display = "Invalid URL: {}", _0)]
InvalidUrl(String),
}
#[derive(Debug, Clone)]
pub struct DataSource {
protocol: String,
host: String,
port: u16,
database_name: Option<String>,
authentication: Authentication,
timeout: Duration,
}
impl DataSource {
pub fn from_url(url: &Url) -> Self {
let protocol = url.scheme();
let host = url.host_str().unwrap_or(DEFAULT_HOST);
let port = url.port().unwrap_or(DEFAULT_PORT);
let username = if url.username().is_empty() {
DEFAULT_USERNAME
} else {
url.username()
};
let password = if let Some(passwd) = url.password() {
passwd.to_owned()
} else if let Ok(passwd) = env::var(ENV_ROOT_PASSWORD) {
passwd
} else {
DEFAULT_PASSWORD.to_owned()
};
let database_name = None;
DataSource {
protocol: protocol.to_owned(),
host: host.to_owned(),
port,
database_name,
authentication: Authentication::Basic(Credentials::new(
username.to_owned(),
password)),
timeout: Duration::from_secs(DEFAULT_TIMEOUT),
}
}
pub fn use_database<DbName>(&self, database_name: DbName) -> Self
where DbName: Into<String>
{
let database_name = database_name.into();
let database_name = if database_name.is_empty() {
None
} else {
Some(database_name.to_owned())
};
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
database_name,
authentication: self.authentication.clone(),
timeout: self.timeout,
}
}
pub fn use_default_database(&self) -> Self {
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
database_name: None,
authentication: self.authentication.clone(),
timeout: self.timeout,
}
}
pub fn with_basic_authentication(&self, username: &str, password: &str) -> Self {
let authentication = Authentication::Basic(
Credentials::new(username.to_owned(), password.to_owned())
);
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
database_name: self.database_name.clone(),
authentication,
timeout: self.timeout,
}
}
pub fn with_authentication(&self, authentication: Authentication) -> Self {
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
database_name: self.database_name.clone(),
authentication,
timeout: self.timeout,
}
}
pub fn without_authentication(&self) -> Self {
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
database_name: self.database_name.clone(),
authentication: Authentication::None,
timeout: self.timeout,
}
}
pub fn with_timeout<D>(&self, timeout: D) -> Self
where D: Into<Duration>
{
DataSource {
protocol: self.protocol.clone(),
host: self.host.clone(),
port: self.port,
authentication: self.authentication.clone(),
database_name: self.database_name.clone(),
timeout: timeout.into(),
}
}
pub fn protocol(&self) -> &str {
&self.protocol
}
pub fn host(&self) -> &str {
&self.host
}
pub fn port(&self) -> u16 {
self.port
}
pub fn database_name(&self) -> Option<&String> {
self.database_name.as_ref()
}
pub fn authentication(&self) -> &Authentication {
&self.authentication
}
pub fn timeout(&self) -> &Duration {
&self.timeout
}
}
impl Default for DataSource {
fn default() -> Self {
DataSource {
protocol: DEFAULT_PROTOCOL.to_owned(),
host: DEFAULT_HOST.to_owned(),
port: DEFAULT_PORT,
authentication: Authentication::Basic(Credentials::new(
DEFAULT_USERNAME.to_owned(),
DEFAULT_PASSWORD.to_owned())),
database_name: None,
timeout: Duration::from_secs(DEFAULT_TIMEOUT),
}
}
}
impl FromStr for DataSource {
type Err = Error;
fn from_str(url_str: &str) -> Result<Self, <Self as FromStr>::Err> {
Url::parse(url_str)
.map_err(|cause| Error::InvalidUrl(cause.to_string()))
.map(|url| DataSource::from_url(&url))
}
}