Skip to main content

de_mls/conversation/
display.rs

1use std::fmt;
2
3use crate::protos::de_mls::messages::v1::{
4    ConversationUpdateRequest, ViolationEvidence, ViolationType, app_message,
5    conversation_update_request,
6};
7
8// ─────────────────────────── Member Role ───────────────────────────
9
10/// A member's steward-list role for a given epoch.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum MemberRole {
13    /// Epoch steward for this epoch.
14    EpochSteward,
15    /// Backup steward for this epoch.
16    BackupSteward,
17    /// On the steward list but not in the epoch or backup slot.
18    Steward,
19    /// Not on the steward list.
20    Member,
21}
22
23impl fmt::Display for MemberRole {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        match self {
26            MemberRole::EpochSteward => write!(f, "epoch_steward"),
27            MemberRole::BackupSteward => write!(f, "backup_steward"),
28            MemberRole::Steward => write!(f, "steward"),
29            MemberRole::Member => write!(f, "member"),
30        }
31    }
32}
33
34// ─────────────────────────── Message Type Labels ───────────────────────────
35
36/// String constants shared with the UI so string-keyed dispatch stays typo-safe.
37pub mod message_types {
38    pub const CONVERSATION_MESSAGE: &str = "ConversationMessage";
39    pub const MEMBERSHIP_CHANGE: &str = "MembershipChange";
40    pub const BAN_REQUEST: &str = "BanRequest";
41    pub const PROPOSAL: &str = "Proposal";
42    pub const VOTE: &str = "Vote";
43    pub const VOTE_PAYLOAD: &str = "VotePayload";
44    pub const USER_VOTE: &str = "UserVote";
45    pub const PROPOSAL_ADDED: &str = "ProposalAdded";
46    pub const COMMIT_CANDIDATE: &str = "CommitCandidate";
47    pub const CONVERSATION_SYNC: &str = "ConversationSync";
48    pub const MEMBER_WELCOME: &str = "MemberWelcome";
49    pub const UNKNOWN: &str = "Unknown";
50}
51
52/// Maps a protobuf message to a stable label string for the UI.
53pub trait MessageType {
54    fn message_type(&self) -> &'static str;
55}
56
57impl MessageType for app_message::Payload {
58    fn message_type(&self) -> &'static str {
59        match self {
60            app_message::Payload::ConversationMessage(_) => message_types::CONVERSATION_MESSAGE,
61            app_message::Payload::MembershipChange(_) => message_types::MEMBERSHIP_CHANGE,
62            app_message::Payload::BanRequest(_) => message_types::BAN_REQUEST,
63            app_message::Payload::Proposal(_) => message_types::PROPOSAL,
64            app_message::Payload::Vote(_) => message_types::VOTE,
65            app_message::Payload::VotePayload(_) => message_types::VOTE_PAYLOAD,
66            app_message::Payload::UserVote(_) => message_types::USER_VOTE,
67            app_message::Payload::ProposalAdded(_) => message_types::PROPOSAL_ADDED,
68            app_message::Payload::CommitCandidate(_) => message_types::COMMIT_CANDIDATE,
69            app_message::Payload::ConversationSync(_) => message_types::CONVERSATION_SYNC,
70            app_message::Payload::MemberWelcome(_) => message_types::MEMBER_WELCOME,
71        }
72    }
73}
74
75impl MessageType for ConversationUpdateRequest {
76    fn message_type(&self) -> &'static str {
77        match &self.payload {
78            Some(conversation_update_request::Payload::MemberInvite(_)) => "Add Member",
79            Some(conversation_update_request::Payload::RemoveMember(_)) => "Remove Member",
80            Some(conversation_update_request::Payload::EmergencyCriteria(ec)) => ec
81                .evidence
82                .as_ref()
83                .map(|e| match ViolationType::try_from(e.violation_type) {
84                    Ok(ViolationType::BrokenCommit) => "Emergency: Broken Commit",
85                    Ok(ViolationType::BrokenMlsProposal) => "Emergency: Broken MLS Proposal",
86                    Ok(ViolationType::CensorshipInactivity) => "Emergency: Censorship/Inactivity",
87                    Ok(ViolationType::ScoreBelowThreshold) => "Emergency: Score Below Threshold",
88                    _ => "Emergency: Unknown Violation",
89                })
90                .unwrap_or("Emergency: Unknown Violation"),
91            Some(conversation_update_request::Payload::StewardElection(_)) => "Steward Election",
92            _ => "Unknown",
93        }
94    }
95}
96
97// ─────────────────────────── Violation Labels ───────────────────────────
98
99impl ViolationEvidence {
100    /// Human-readable label for the violation type.
101    pub fn violation_type_label(&self) -> &'static str {
102        match ViolationType::try_from(self.violation_type) {
103            Ok(ViolationType::BrokenCommit) => "Broken Commit",
104            Ok(ViolationType::BrokenMlsProposal) => "Broken MLS Proposal",
105            Ok(ViolationType::CensorshipInactivity) => "Censorship/Inactivity",
106            Ok(ViolationType::ScoreBelowThreshold) => "Score Below Threshold",
107            _ => "Unknown Violation",
108        }
109    }
110}