use std::env::VarError;
use std::time::Duration;
use reliar_core::SettingsError;
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default, deny_unknown_fields))]
#[non_exhaustive]
pub struct PostgresOutboxSettings {
#[cfg_attr(
feature = "serde",
serde(rename = "statement_timeout_ms", with = "crate::duration_serde")
)]
pub statement_timeout: Duration,
}
impl Default for PostgresOutboxSettings {
fn default() -> Self {
Self {
statement_timeout: Duration::ZERO,
}
}
}
impl PostgresOutboxSettings {
#[must_use]
pub const fn statement_timeout(mut self, timeout: Duration) -> Self {
self.statement_timeout = timeout;
self
}
pub fn from_env(prefix: &str) -> Result<Self, SettingsError> {
let mut settings = Self::default();
if let Some(v) = env_duration_ms(prefix, "STATEMENT_TIMEOUT_MS")? {
settings.statement_timeout = v;
}
Ok(settings)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default, deny_unknown_fields))]
#[non_exhaustive]
pub struct PostgresInboxSettings {
#[cfg_attr(
feature = "serde",
serde(rename = "statement_timeout_ms", with = "crate::duration_serde")
)]
pub statement_timeout: Duration,
pub max_attempts: u32,
}
const DEFAULT_MAX_ATTEMPTS: u32 = 10;
impl Default for PostgresInboxSettings {
fn default() -> Self {
Self {
statement_timeout: Duration::ZERO,
max_attempts: DEFAULT_MAX_ATTEMPTS,
}
}
}
impl PostgresInboxSettings {
#[must_use]
pub const fn statement_timeout(mut self, timeout: Duration) -> Self {
self.statement_timeout = timeout;
self
}
#[must_use]
pub const fn max_attempts(mut self, max_attempts: u32) -> Self {
self.max_attempts = max_attempts;
self
}
pub fn from_env(prefix: &str) -> Result<Self, SettingsError> {
let mut settings = Self::default();
if let Some(v) = env_duration_ms(prefix, "STATEMENT_TIMEOUT_MS")? {
settings.statement_timeout = v;
}
if let Some(v) = env_u32(prefix, "MAX_ATTEMPTS")? {
settings.max_attempts = v;
}
Ok(settings)
}
pub(crate) fn validate(&self) -> Result<(), crate::PostgresInboxError> {
if self.max_attempts == 0 {
return Err(crate::PostgresInboxError::InvalidSettings {
message: "max_attempts must not be 0 (reads as \"no retries\"; use u32::MAX for \
unbounded)"
.to_owned(),
});
}
Ok(())
}
}
fn env_duration_ms(prefix: &str, suffix: &str) -> Result<Option<Duration>, SettingsError> {
let key = format!("{prefix}{suffix}");
let raw = match std::env::var(&key) {
Ok(value) => value,
Err(VarError::NotPresent) => return Ok(None),
Err(VarError::NotUnicode(_)) => return Err(SettingsError::parse(key, "a UTF-8 string")),
};
let ms = raw
.trim()
.parse::<u64>()
.map_err(|_| SettingsError::parse(key, "milliseconds"))?;
Ok(Some(Duration::from_millis(ms)))
}
fn env_u32(prefix: &str, suffix: &str) -> Result<Option<u32>, SettingsError> {
let key = format!("{prefix}{suffix}");
let raw = match std::env::var(&key) {
Ok(value) => value,
Err(VarError::NotPresent) => return Ok(None),
Err(VarError::NotUnicode(_)) => return Err(SettingsError::parse(key, "a UTF-8 string")),
};
raw.trim()
.parse::<u32>()
.map(Some)
.map_err(|_| SettingsError::parse(key, "u32"))
}