diff --git a/api/src/email.rs b/api/src/email.rs index ab213c5..92e888b 100644 --- a/api/src/email.rs +++ b/api/src/email.rs @@ -69,26 +69,42 @@ pub fn confirm(brand: &str, accent: &str, logo: &str, account: &str, code: &str) } } +/// What an inactivity-expiry warning is about. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ExpiryTarget { + Account, + Channel, +} + +impl ExpiryTarget { + fn word(self) -> &'static str { + match self { + Self::Account => "account", + Self::Channel => "channel", + } + } +} + // Warn the owner of an account or channel that inactivity will soon expire it. -// `kind` is "account" or "channel", `remaining` a human span ("7 days"). The -// remaining span takes the prominent code slot. -pub fn expiry_warning(brand: &str, accent: &str, logo: &str, kind: &str, name: &str, remaining: &str) -> Mail { - let keep = if kind == "channel" { +// `remaining` is a human span ("7 days") and takes the prominent code slot. +pub fn expiry_warning(brand: &str, accent: &str, logo: &str, kind: ExpiryTarget, name: &str, remaining: &str) -> Mail { + let word = kind.word(); + let keep = if kind == ExpiryTarget::Channel { "To keep it, have a member join the channel before then. Otherwise it will be removed." } else { "To keep it, just identify to it before then. Otherwise it will be removed." }; Mail { - subject: format!("Your {kind} {name} is about to expire"), + subject: format!("Your {word} {name} is about to expire"), text: format!( - "Your {kind} {name} has been inactive and will expire in {remaining}.\n{keep}\n" + "Your {word} {name} has been inactive and will expire in {remaining}.\n{keep}\n" ), html: render( brand, accent, logo, "About to expire", - &format!("Your {kind} {name} has been inactive and will expire in {remaining}."), + &format!("Your {word} {name} has been inactive and will expire in {remaining}."), remaining, keep, ), diff --git a/modules/gameserv/src/board.rs b/modules/gameserv/src/board.rs index d1c8f44..c6e9de7 100644 --- a/modules/gameserv/src/board.rs +++ b/modules/gameserv/src/board.rs @@ -289,11 +289,10 @@ pub fn terminal(g: &Game) -> Option { } full_or_ongoing(b) } - Board::Chess(st) => match chess::over(st).as_str() { - "w" => Some(Outcome::Win(Side::A)), - "b" => Some(Outcome::Win(Side::B)), - "draw" => Some(Outcome::Draw), - _ => None, + Board::Chess(st) => match chess::over(st) { + None => None, + Some(chess::Ending::Draw) => Some(Outcome::Draw), + Some(chess::Ending::Checkmate(c)) => Some(Outcome::Win(if c == b'w' { Side::A } else { Side::B })), }, } } diff --git a/modules/gameserv/src/chess.rs b/modules/gameserv/src/chess.rs index e415f53..aa094ab 100644 --- a/modules/gameserv/src/chess.rs +++ b/modules/gameserv/src/chess.rs @@ -476,16 +476,24 @@ pub fn legal(st: &State) -> Vec { .collect() } -/// `""` ongoing; `"draw"` for stalemate; or the winning side (`"w"`/`"b"`) on -/// checkmate. -pub fn over(st: &State) -> String { +/// How a position has ended. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Ending { + Draw, + Checkmate(u8), // the winning colour byte: b'w' or b'b' +} + +/// `None` while the side to move still has a legal move; else the ending +/// (stalemate → `Draw`, checkmate → `Checkmate(winner)`). +pub fn over(st: &State) -> Option { if !legal(st).is_empty() { - return String::new(); + return None; } if in_check(st, st.turn) { - return if st.turn == b'w' { "b" } else { "w" }.to_string(); + Some(Ending::Checkmate(if st.turn == b'w' { b'b' } else { b'w' })) + } else { + Some(Ending::Draw) } - "draw".to_string() } fn name_sq(s: i32) -> String { diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 90c0b03..3c4ecb3 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -703,14 +703,14 @@ impl Engine { if let Some(lead) = self.expire_warn.filter(|_| self.db.email_enabled()) { if let Some(ttl) = self.account_ttl { for (account, email, left) in self.db.accounts_to_warn(now, ttl, lead) { - let mail = echo_api::email::expiry_warning(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), "account", &account, &human_duration(left)); + let mail = echo_api::email::expiry_warning(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), echo_api::email::ExpiryTarget::Account, &account, &human_duration(left)); self.emit_irc(NetAction::SendEmail { to: email, subject: mail.subject, text: mail.text, html: Some(mail.html) }); self.db.mark_account_warned(&account); } } if let Some(ttl) = self.channel_ttl { for (channel, email, left) in self.db.channels_to_warn(now, ttl, lead) { - let mail = echo_api::email::expiry_warning(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), "channel", &channel, &human_duration(left)); + let mail = echo_api::email::expiry_warning(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), echo_api::email::ExpiryTarget::Channel, &channel, &human_duration(left)); self.emit_irc(NetAction::SendEmail { to: email, subject: mail.subject, text: mail.text, html: Some(mail.html) }); self.db.mark_channel_warned(&channel); }