use async_trait::async_trait;
use super::admin_topics::AdminChannel;
pub const DEFAULT_LOCALE: &str = "tr";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RecipientRef {
Subject(String),
AccountId(i64),
Admin(AdminChannel),
}
impl RecipientRef {
pub fn log_label(&self) -> String {
match self {
Self::Subject(sub) => {
let head: String = sub.chars().take(8).collect();
format!("sub={head}…")
}
Self::AccountId(id) => format!("accountId={id}"),
Self::Admin(channel) => channel.to_string(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Audience {
pub subscriber_id: String,
pub locale: String,
}
impl Audience {
pub fn new(subscriber_id: impl Into<String>, locale: impl Into<String>) -> Self {
Self {
subscriber_id: subscriber_id.into(),
locale: locale.into(),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum AudienceError {
#[error("no notification target for {0}")]
NotFound(String),
#[error("notification target lookup failed for {reference}: {message}")]
Unavailable { reference: String, message: String },
#[error("notification audience resolver is not configured")]
NotConfigured,
}
#[async_trait]
pub trait AudienceResolver: Send + Sync {
async fn resolve(&self, reference: &RecipientRef) -> Result<Audience, AudienceError>;
}
pub fn normalize_locale(raw: &str) -> Option<String> {
let language = raw
.trim()
.split(['-', '_'])
.next()
.unwrap_or_default()
.to_ascii_lowercase();
if language.is_empty() {
None
} else {
Some(language)
}
}
pub struct StaticAudienceResolver(Audience);
impl StaticAudienceResolver {
pub fn new(audience: Audience) -> Self {
Self(audience)
}
}
#[async_trait]
impl AudienceResolver for StaticAudienceResolver {
async fn resolve(&self, _reference: &RecipientRef) -> Result<Audience, AudienceError> {
Ok(self.0.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn locale_normalization_matches_notification_translation_keys() {
assert_eq!(normalize_locale("EN"), Some("en".to_string()));
assert_eq!(normalize_locale("en_US"), Some("en".to_string()));
assert_eq!(normalize_locale("tr-TR"), Some("tr".to_string()));
assert_eq!(normalize_locale(" "), None);
}
#[test]
fn log_labels_do_not_spell_out_the_subject() {
let label = RecipientRef::Subject("0192f3ab-cdef-7000-8000-000000000001".into()).log_label();
assert_eq!(label, "sub=0192f3ab…");
assert_eq!(RecipientRef::AccountId(42).log_label(), "accountId=42");
}
}