aboutsummaryrefslogtreecommitdiffstats
path: root/lib/warden-stack/src/postgres.rs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/warden-stack/src/postgres.rs')
-rw-r--r--lib/warden-stack/src/postgres.rs137
1 files changed, 137 insertions, 0 deletions
diff --git a/lib/warden-stack/src/postgres.rs b/lib/warden-stack/src/postgres.rs
new file mode 100644
index 0000000..3264368
--- /dev/null
+++ b/lib/warden-stack/src/postgres.rs
@@ -0,0 +1,137 @@
+use std::sync::Arc;
+
+use secrecy::{ExposeSecret, SecretString};
+use serde::Deserialize;
+use url::Url;
+
+use crate::{
+ ServicesBuilder,
+ services_builder::{IsUnset, SetPostgres, State},
+};
+
+#[derive(Debug, Deserialize, Clone)]
+pub struct PostgresConfig {
+ #[serde(default = "default_pool_size")]
+ pool_size: u32,
+ #[serde(default = "default_port")]
+ port: u32,
+ name: Arc<str>,
+ host: Arc<str>,
+ #[serde(default = "user")]
+ user: Arc<str>,
+ password: SecretString,
+}
+
+fn default_pool_size() -> u32 {
+ 100
+}
+
+fn user() -> Arc<str> {
+ "postgres".into()
+}
+
+fn default_port() -> u32 {
+ 5432
+}
+
+impl PostgresConfig {
+ // Getter for size
+ pub fn pool_size(&self) -> u32 {
+ self.pool_size
+ }
+
+ // Getter for port
+ pub fn port(&self) -> u32 {
+ self.port
+ }
+
+ // Getter for name
+ pub fn name(&self) -> &str {
+ self.name.as_ref()
+ }
+
+ // Getter for host
+ pub fn host(&self) -> &str {
+ self.host.as_ref()
+ }
+
+ // Getter for username
+ pub fn username(&self) -> &str {
+ self.user.as_ref()
+ }
+
+ // Getter for password (you may want to return a reference or handle it differently)
+ pub fn password(&self) -> &SecretString {
+ &self.password
+ }
+
+ pub(crate) fn connection_string(&self) -> Result<Url, crate::ServiceError> {
+ Url::parse(&format!(
+ "postgres://{}:{}@{}:{}/{}",
+ self.user,
+ self.password.expose_secret(),
+ self.host,
+ self.port,
+ self.name
+ ))
+ .map_err(|e| crate::ServiceError::Configuration(e.to_string()))
+ }
+}
+
+impl<S: State> ServicesBuilder<S> {
+ pub async fn postgres(
+ self,
+ config: &PostgresConfig,
+ ) -> Result<ServicesBuilder<SetPostgres<S>>, crate::ServiceError>
+ where
+ S::Postgres: IsUnset,
+ {
+ let pg = sqlx::postgres::PgPoolOptions::new()
+ // The default connection limit for a Postgres server is 100 connections, with 3 reserved for superusers.
+ //
+ // If you're deploying your application with multiple replicas, then the total
+ // across all replicas should not exceed the Postgres connection limit
+ // (max_connections postgresql.conf).
+ .max_connections(config.pool_size)
+ .connect(config.connection_string()?.as_ref())
+ .await?;
+ Ok(self.pg_internal(pg))
+ }
+}
+
+#[cfg(all(test, target_os = "linux"))]
+mod test {
+ use super::*;
+ use crate::Services;
+
+ #[tokio::test]
+ async fn docker_stack_db() {
+ let port = default_port();
+ let name = "";
+ let host = "localhost";
+ let user = user();
+ let pool_size = default_pool_size();
+ let password = "postgres";
+
+ let config = PostgresConfig {
+ pool_size,
+ port,
+ name: name.into(),
+ host: host.into(),
+ user: user.clone(),
+ password: secrecy::SecretString::new(password.into()),
+ };
+
+ assert_eq!(config.name(), name);
+ assert_eq!(config.pool_size(), pool_size);
+ assert_eq!(config.username(), user.as_ref());
+ assert_eq!(config.host(), host);
+ assert_eq!(config.port(), port);
+
+ assert_eq!(config.password().expose_secret(), password);
+
+ let service = Services::builder().postgres(&config).await;
+
+ assert!(service.is_ok());
+ }
+}