use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AdminChannel {
Alert,
Info,
}
impl AdminChannel {
pub const ALL: [AdminChannel; 2] = [AdminChannel::Alert, AdminChannel::Info];
pub fn as_str(self) -> &'static str {
match self {
Self::Alert => "ADMIN_ALERT",
Self::Info => "ADMIN_INFO",
}
}
pub fn env_key(self) -> &'static str {
match self {
Self::Alert => "NOTIFICATION_TOPIC_ADMIN_ALERT",
Self::Info => "NOTIFICATION_TOPIC_ADMIN_INFO",
}
}
pub fn default_topic_key(self) -> &'static str {
match self {
Self::Alert => "admin-alert",
Self::Info => "admin-info",
}
}
}
impl fmt::Display for AdminChannel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct AdminTopics {
alert: String,
info: String,
}
impl AdminTopics {
pub fn from_env() -> Self {
Self {
alert: topic_from_env(AdminChannel::Alert),
info: topic_from_env(AdminChannel::Info),
}
}
pub fn new(alert: impl Into<String>, info: impl Into<String>) -> Self {
Self {
alert: alert.into(),
info: info.into(),
}
}
pub fn key(&self, channel: AdminChannel) -> &str {
match channel {
AdminChannel::Alert => &self.alert,
AdminChannel::Info => &self.info,
}
}
}
impl Default for AdminTopics {
fn default() -> Self {
Self::new(
AdminChannel::Alert.default_topic_key(),
AdminChannel::Info.default_topic_key(),
)
}
}
fn topic_from_env(channel: AdminChannel) -> String {
std::env::var(channel.env_key())
.ok()
.map(|key| key.trim().to_string())
.filter(|key| !key.is_empty())
.unwrap_or_else(|| channel.default_topic_key().to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_channel_names_its_own_environment_variable_and_default() {
assert_eq!(AdminChannel::Alert.as_str(), "ADMIN_ALERT");
assert_eq!(
AdminChannel::Alert.env_key(),
"NOTIFICATION_TOPIC_ADMIN_ALERT"
);
assert_eq!(AdminChannel::Alert.default_topic_key(), "admin-alert");
assert_eq!(AdminChannel::Info.env_key(), "NOTIFICATION_TOPIC_ADMIN_INFO");
}
#[test]
fn channels_do_not_share_a_default_topic() {
let topics = AdminTopics::default();
let keys: std::collections::HashSet<_> =
AdminChannel::ALL.iter().map(|c| topics.key(*c)).collect();
assert_eq!(keys.len(), AdminChannel::ALL.len());
}
#[test]
fn an_explicit_mapping_wins_over_the_defaults() {
let topics = AdminTopics::new("topic-1", "topic-2");
assert_eq!(topics.key(AdminChannel::Alert), "topic-1");
assert_eq!(topics.key(AdminChannel::Info), "topic-2");
}
}