use crate::models::{Event, Party};
pub fn mask_event(event: &Event) -> Event {
let mut masked = event.clone();
for id in &mut masked.identifiers {
id.value = mask_value(&id.value, 4);
}
mask_parties(&mut masked.attendees);
mask_parties(&mut masked.performers);
mask_parties(&mut masked.organizers);
mask_parties(&mut masked.sponsors);
mask_parties(&mut masked.funders);
mask_parties(&mut masked.contributors);
masked
}
fn mask_parties(parties: &mut [Party]) {
for p in parties {
if let Some(ref email) = p.email {
p.email = Some(mask_value(email, 4));
}
p.id = None;
}
}
fn mask_value(value: &str, visible_chars: usize) -> String {
if value.len() <= visible_chars {
return value.to_string();
}
let visible_start = value.len() - visible_chars;
let masked_part: String = value[..visible_start]
.chars()
.map(|c| if c.is_alphanumeric() { '*' } else { c })
.collect();
format!("{masked_part}{}", &value[visible_start..])
}
pub fn has_active_consent(
consents: &[crate::models::Consent],
consent_type: crate::models::ConsentType,
) -> bool {
let today = chrono::Utc::now().date_naive();
consents.iter().any(|c| {
c.consent_type == consent_type
&& c.status == crate::models::ConsentStatus::Active
&& c.expiry_date.map_or(true, |exp| exp >= today)
})
}
pub fn export_event_data(event: &Event) -> serde_json::Value {
serde_json::to_value(event).unwrap_or(serde_json::Value::Null)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::models::{Identifier, IdentifierType, Party, PartyKind};
use chrono::{TimeZone, Utc};
#[test]
fn mask_value_keeps_last_4() {
assert_eq!(mask_value("ABC-12345", 4), "***-*2345");
assert_eq!(mask_value("short", 10), "short");
}
#[test]
fn mask_event_masks_identifiers() {
let mut event = Event::new("Concert", Utc.with_ymd_and_hms(2026, 3, 1, 9, 0, 0).unwrap());
event.identifiers.push(Identifier::new(
IdentifierType::TicketNumber,
"sys".into(),
"T-987654".into(),
));
let masked = mask_event(&event);
assert_eq!(masked.identifiers[0].value, "*-**7654");
}
#[test]
fn mask_event_masks_party_emails() {
let mut event = Event::new("X", Utc.with_ymd_and_hms(2026, 3, 1, 9, 0, 0).unwrap());
event.attendees.push(Party {
kind: PartyKind::Person,
id: Some(uuid::Uuid::new_v4()),
name: "Alice".into(),
email: Some("alice@example.test".into()),
url: None,
});
let masked = mask_event(&event);
assert!(masked.attendees[0].email.as_ref().unwrap().ends_with("test"));
assert!(masked.attendees[0].email.as_ref().unwrap().contains('*'));
assert!(masked.attendees[0].id.is_none());
assert_eq!(masked.attendees[0].name, "Alice");
}
#[test]
fn export_includes_all_fields() {
let event = Event::new("X", Utc.with_ymd_and_hms(2026, 3, 1, 9, 0, 0).unwrap());
let exported = export_event_data(&event);
let obj = exported.as_object().unwrap();
assert!(obj.contains_key("name"));
assert!(obj.contains_key("start_date"));
assert!(obj.contains_key("event_status"));
}
}