use bb8::{Builder as PoolBuilder, Pool};
use tonic::transport::ClientTlsConfig;
use crate::{Client, DatabaseId, Error, InstanceId, ProjectId, SessionManager};
use derive_builder::Builder;
#[derive(Builder, Debug)]
#[builder(pattern = "owned", build_fn(error = "crate::Error"))]
pub struct Config {
#[builder(setter(strip_option, into), default)]
endpoint: Option<String>,
#[builder(setter(strip_option), default = "Some(ClientTlsConfig::default())")]
tls_config: Option<ClientTlsConfig>,
#[builder(setter(strip_option, into), default)]
project: Option<String>,
#[builder(setter(strip_option, into))]
instance: String,
#[builder(setter(strip_option, into))]
database: String,
#[builder(setter(strip_option, into), default)]
credentials_file: Option<String>,
#[builder(setter(strip_option), default)]
session_pool_config: Option<SessionPoolConfig>,
}
impl Config {
pub fn builder() -> ConfigBuilder {
ConfigBuilder::default()
}
pub async fn connect(self) -> Result<Client, Error> {
let auth = if self.tls_config.is_none() {
None
} else {
match self.credentials_file {
Some(file) => Some(gcp_auth::CustomServiceAccount::from_file(file)?.into()),
None => Some(gcp_auth::AuthenticationManager::new().await?),
}
};
let project_id = match self.project {
Some(project) => project,
None => {
if let Some(auth) = auth.as_ref() {
auth.project_id().await?
} else {
return Err(Error::Config("missing project id".to_string()));
}
}
};
let database_id = DatabaseId::new(
InstanceId::new(ProjectId::new(&project_id), &self.instance),
&self.database,
);
let connection =
crate::connection::grpc::connect(self.endpoint, self.tls_config, auth, database_id)
.await?;
let pool = self
.session_pool_config
.unwrap_or_default()
.build()
.build(SessionManager::new(connection.clone()))
.await?;
Ok(Client::connect(connection, pool))
}
}
impl ConfigBuilder {
#[must_use]
pub fn disable_tls(self) -> Self {
Self {
tls_config: Some(None),
..self
}
}
#[must_use]
pub fn with_emulator_host(self, endpoint: String) -> Self {
self.endpoint(endpoint).disable_tls()
}
#[must_use]
pub fn with_emulator_grpc_port(self, port: u16) -> Self {
self.with_emulator_host(format!("http://localhost:{}", port))
}
pub async fn connect(self) -> Result<Client, Error> {
self.build()?.connect().await
}
}
#[derive(Builder, Default, Debug)]
#[builder(pattern = "owned", build_fn(error = "crate::Error"))]
pub struct SessionPoolConfig {
#[builder(setter(strip_option), default)]
max_size: Option<u32>,
#[builder(setter(strip_option), default)]
min_idle: Option<u32>,
}
impl SessionPoolConfig {
pub fn builder() -> SessionPoolConfigBuilder {
SessionPoolConfigBuilder::default()
}
fn build(self) -> PoolBuilder<SessionManager> {
let mut builder = Pool::builder().test_on_check_out(false);
if let Some(max_size) = self.max_size {
builder = builder.max_size(max_size);
}
builder.min_idle(self.min_idle)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_config_database() {
let cfg = Config::builder()
.project("project")
.instance("instance")
.database("database")
.build()
.unwrap();
assert_eq!(cfg.project, Some("project".to_string()));
assert_eq!(cfg.instance, "instance".to_string());
assert_eq!(cfg.database, "database".to_string());
}
#[test]
fn test_config_endpoint() {
let cfg = Config::builder().endpoint("endpoint");
assert_eq!(cfg.endpoint, Some(Some("endpoint".to_string())))
}
#[test]
fn test_session_pool_config() {
let built = SessionPoolConfig::builder()
.max_size(10)
.min_idle(100)
.build()
.unwrap();
assert_eq!(built.max_size, Some(10));
assert_eq!(built.min_idle, Some(100));
}
}