Skip to main content

headless_lms_server/domain/credit_registration/
mail_status.rs

1//! How the student and teacher views show the mails a registration row sent.
2
3use headless_lms_models::credit_registrations::CreditRegistrationState;
4use headless_lms_models::email_deliveries::EmailSendStatus;
5use headless_lms_models::library::credit_registration::student_notifications::{
6    CreditRegistrationNotificationKind, RegistrationNotificationEmail,
7};
8use utoipa::ToSchema;
9
10use crate::prelude::*;
11
12/// Where one of the two terminal-state mails stands, as a row shows it. No address: these go to
13/// the account's own, which the reader either owns or already sees.
14#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, ToSchema)]
15pub struct NotificationEmailStatus {
16    pub kind: CreditRegistrationNotificationKind,
17    pub email_send_status: EmailSendStatus,
18    pub sent_at: Option<DateTime<Utc>>,
19}
20
21impl NotificationEmailStatus {
22    /// The mail that belongs with the row's current state, or `None` for a state that has none: a
23    /// row shows at most one line, and an old action-needed mail on a since-registered row would
24    /// contradict the badge above it.
25    pub fn for_state(
26        state: CreditRegistrationState,
27        credit_registration_id: Uuid,
28        mails: &[RegistrationNotificationEmail],
29    ) -> Option<Self> {
30        let wanted = CreditRegistrationNotificationKind::for_state(state)?;
31        let mail = mails.iter().find(|mail| {
32            mail.credit_registration_id == credit_registration_id && mail.kind == wanted
33        })?;
34        Some(Self {
35            kind: mail.kind,
36            email_send_status: mail.send_status.email_send_status,
37            sent_at: mail.send_status.sent_at,
38        })
39    }
40}
41
42/// Keeps the domain and drops the local part: enough to recognise which mailbox to open, not a new
43/// disclosure of an address. Teachers get the same masking; only admins see an address in full.
44pub fn mask_email(email: &str) -> String {
45    match email.split_once('@') {
46        Some((_, domain)) => format!("...@{domain}"),
47        None => "...".to_string(),
48    }
49}