use std::fmt;
use std::str::FromStr;
use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MediationSessionState {
Opening,
AwaitingResponse,
Classified,
FollowUpPending,
SummaryPending,
SummaryDelivered,
EscalationRecommended,
SupersededByHuman,
Closed,
}
impl MediationSessionState {
pub fn can_transition_to(self, next: MediationSessionState) -> bool {
use MediationSessionState::*;
matches!(
(self, next),
(Opening, AwaitingResponse)
| (AwaitingResponse, Classified)
| (Classified, FollowUpPending)
| (Classified, SummaryPending)
| (FollowUpPending, AwaitingResponse)
| (SummaryPending, SummaryDelivered)
| (SummaryDelivered, Closed)
| (Opening, EscalationRecommended)
| (AwaitingResponse, EscalationRecommended)
| (Classified, EscalationRecommended)
| (FollowUpPending, EscalationRecommended)
| (SummaryPending, EscalationRecommended)
| (EscalationRecommended, Closed)
| (Opening, SupersededByHuman)
| (AwaitingResponse, SupersededByHuman)
| (Classified, SupersededByHuman)
| (FollowUpPending, SupersededByHuman)
| (SummaryPending, SupersededByHuman)
| (SupersededByHuman, Closed)
)
}
pub fn is_terminal(self) -> bool {
matches!(self, MediationSessionState::Closed)
}
}
impl fmt::Display for MediationSessionState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use MediationSessionState::*;
let s = match self {
Opening => "opening",
AwaitingResponse => "awaiting_response",
Classified => "classified",
FollowUpPending => "follow_up_pending",
SummaryPending => "summary_pending",
SummaryDelivered => "summary_delivered",
EscalationRecommended => "escalation_recommended",
SupersededByHuman => "superseded_by_human",
Closed => "closed",
};
f.write_str(s)
}
}
impl FromStr for MediationSessionState {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
use MediationSessionState::*;
match s {
"opening" => Ok(Opening),
"awaiting_response" => Ok(AwaitingResponse),
"classified" => Ok(Classified),
"follow_up_pending" => Ok(FollowUpPending),
"summary_pending" => Ok(SummaryPending),
"summary_delivered" => Ok(SummaryDelivered),
"escalation_recommended" => Ok(EscalationRecommended),
"superseded_by_human" => Ok(SupersededByHuman),
"closed" => Ok(Closed),
other => Err(Error::InvalidEvent(format!(
"unknown mediation session state: {other}"
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EscalationTrigger {
ConflictingClaims,
FraudIndicator,
LowConfidence,
PartyUnresponsive,
RoundLimit,
ReasoningUnavailable,
AuthorizationLost,
AuthorityBoundaryAttempt,
MediationTimeout,
PolicyBundleMissing,
InvalidModelOutput,
NotificationFailed,
}
impl fmt::Display for EscalationTrigger {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use EscalationTrigger::*;
let s = match self {
ConflictingClaims => "conflicting_claims",
FraudIndicator => "fraud_indicator",
LowConfidence => "low_confidence",
PartyUnresponsive => "party_unresponsive",
RoundLimit => "round_limit",
ReasoningUnavailable => "reasoning_unavailable",
AuthorizationLost => "authorization_lost",
AuthorityBoundaryAttempt => "authority_boundary_attempt",
MediationTimeout => "mediation_timeout",
PolicyBundleMissing => "policy_bundle_missing",
InvalidModelOutput => "invalid_model_output",
NotificationFailed => "notification_failed",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TranscriptParty {
Buyer,
Seller,
Serbero,
}
impl fmt::Display for TranscriptParty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use TranscriptParty::*;
let s = match self {
Buyer => "buyer",
Seller => "seller",
Serbero => "serbero",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClassificationLabel {
CoordinationFailureResolvable,
ConflictingClaims,
SuspectedFraud,
Unclear,
NotSuitableForMediation,
}
impl fmt::Display for ClassificationLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ClassificationLabel::*;
let s = match self {
CoordinationFailureResolvable => "coordination_failure_resolvable",
ConflictingClaims => "conflicting_claims",
SuspectedFraud => "suspected_fraud",
Unclear => "unclear",
NotSuitableForMediation => "not_suitable_for_mediation",
};
f.write_str(s)
}
}
impl FromStr for ClassificationLabel {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
use ClassificationLabel::*;
match s {
"coordination_failure_resolvable" => Ok(CoordinationFailureResolvable),
"conflicting_claims" => Ok(ConflictingClaims),
"suspected_fraud" => Ok(SuspectedFraud),
"unclear" => Ok(Unclear),
"not_suitable_for_mediation" => Ok(NotSuitableForMediation),
other => Err(Error::InvalidEvent(format!(
"unknown classification label token: {other}"
))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Flag {
FraudRisk,
ConflictingClaims,
LowInfo,
UnresponsiveParty,
AuthorityBoundaryAttempt,
}
impl fmt::Display for Flag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Flag::*;
let s = match self {
FraudRisk => "fraud_risk",
ConflictingClaims => "conflicting_claims",
LowInfo => "low_info",
UnresponsiveParty => "unresponsive_party",
AuthorityBoundaryAttempt => "authority_boundary_attempt",
};
f.write_str(s)
}
}
#[cfg(test)]
mod tests {
use super::MediationSessionState::*;
use super::*;
#[test]
fn allowed_transitions_pass() {
assert!(Opening.can_transition_to(AwaitingResponse));
assert!(AwaitingResponse.can_transition_to(Classified));
assert!(Classified.can_transition_to(FollowUpPending));
assert!(Classified.can_transition_to(SummaryPending));
assert!(FollowUpPending.can_transition_to(AwaitingResponse));
assert!(SummaryPending.can_transition_to(SummaryDelivered));
assert!(SummaryDelivered.can_transition_to(Closed));
assert!(Opening.can_transition_to(EscalationRecommended));
assert!(EscalationRecommended.can_transition_to(Closed));
assert!(AwaitingResponse.can_transition_to(SupersededByHuman));
assert!(SupersededByHuman.can_transition_to(Closed));
}
#[test]
fn disallowed_transitions_reject() {
assert!(!Closed.can_transition_to(Opening));
assert!(!SummaryDelivered.can_transition_to(AwaitingResponse));
assert!(!Opening.can_transition_to(Closed));
assert!(!AwaitingResponse.can_transition_to(AwaitingResponse)); assert!(!Classified.can_transition_to(Opening));
assert!(!EscalationRecommended.can_transition_to(AwaitingResponse));
}
#[test]
fn all_self_transitions_rejected() {
for s in [
Opening,
AwaitingResponse,
Classified,
FollowUpPending,
SummaryPending,
SummaryDelivered,
EscalationRecommended,
SupersededByHuman,
Closed,
] {
assert!(
!s.can_transition_to(s),
"self-transition should be rejected for {s}"
);
}
}
#[test]
fn parse_and_display_roundtrip_for_every_state() {
for s in [
"opening",
"awaiting_response",
"classified",
"follow_up_pending",
"summary_pending",
"summary_delivered",
"escalation_recommended",
"superseded_by_human",
"closed",
] {
let parsed: MediationSessionState = s.parse().unwrap();
assert_eq!(parsed.to_string(), s);
}
}
#[test]
fn parse_and_display_roundtrip_for_every_classification_label() {
for s in [
"coordination_failure_resolvable",
"conflicting_claims",
"suspected_fraud",
"unclear",
"not_suitable_for_mediation",
] {
let parsed: ClassificationLabel = s.parse().unwrap();
assert_eq!(parsed.to_string(), s);
}
assert!("bogus_label".parse::<ClassificationLabel>().is_err());
}
}