use super::zone::Zone;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Side {
Home,
Away,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ShotKind {
Finish,
Header,
LongShot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ShotSource {
Through,
Dribble,
Cutback,
Cross,
Long,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShotOutcome {
Goal,
Saved,
Off,
Blocked,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MatchEventKind {
Pass {
success: bool,
},
TakeOn {
success: bool,
},
Cross {
success: bool,
},
Clearance,
Turnover,
Shot {
kind: ShotKind,
source: ShotSource,
outcome: ShotOutcome,
},
Save {
parried: bool,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MatchEvent {
pub minute: u8,
pub side: Side,
pub zone: Zone,
pub kind: MatchEventKind,
}
impl MatchEvent {
pub fn commentary(&self, side_name: &str) -> String {
let m = self.minute;
let z = self.zone.label();
match self.kind {
MatchEventKind::Pass { success: true } => format!("{m}' {side_name} pick a pass {z}."),
MatchEventKind::Pass { success: false } => {
format!("{m}' {side_name} pass cut out {z}.")
}
MatchEventKind::TakeOn { success: true } => {
format!("{m}' {side_name} beat their marker {z}.")
}
MatchEventKind::TakeOn { success: false } => {
format!("{m}' {side_name} dispossessed {z}.")
}
MatchEventKind::Cross { success: true } => format!("{m}' {side_name} whip in a cross."),
MatchEventKind::Cross { success: false } => {
format!("{m}' {side_name} cross doesn't find a man.")
}
MatchEventKind::Clearance => format!("{m}' Cleared."),
MatchEventKind::Turnover => format!("{m}' Turned over {z}."),
MatchEventKind::Shot { kind, outcome, .. } => {
let k = match kind {
ShotKind::Finish => "shot",
ShotKind::Header => "header",
ShotKind::LongShot => "effort from distance",
};
match outcome {
ShotOutcome::Goal => format!("{m}' GOAL! {side_name} score with a {k}!"),
ShotOutcome::Saved => format!("{m}' {side_name} {k} — saved!"),
ShotOutcome::Off => format!("{m}' {side_name} {k} — off target."),
ShotOutcome::Blocked => format!("{m}' {side_name} {k} — blocked."),
}
}
MatchEventKind::Save { parried: true } => {
format!("{m}' Parried! Loose ball in the box.")
}
MatchEventKind::Save { parried: false } => format!("{m}' Keeper collects."),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn commentary_never_panics_across_the_whole_alphabet() {
let kinds = [
MatchEventKind::Pass { success: true },
MatchEventKind::Pass { success: false },
MatchEventKind::TakeOn { success: true },
MatchEventKind::TakeOn { success: false },
MatchEventKind::Cross { success: true },
MatchEventKind::Cross { success: false },
MatchEventKind::Clearance,
MatchEventKind::Turnover,
MatchEventKind::Shot {
kind: ShotKind::Finish,
source: ShotSource::Through,
outcome: ShotOutcome::Goal,
},
MatchEventKind::Shot {
kind: ShotKind::Header,
source: ShotSource::Cross,
outcome: ShotOutcome::Saved,
},
MatchEventKind::Shot {
kind: ShotKind::LongShot,
source: ShotSource::Long,
outcome: ShotOutcome::Off,
},
MatchEventKind::Shot {
kind: ShotKind::Finish,
source: ShotSource::Cutback,
outcome: ShotOutcome::Blocked,
},
MatchEventKind::Save { parried: true },
MatchEventKind::Save { parried: false },
];
for kind in kinds {
let event = MatchEvent {
minute: 42,
side: Side::Home,
zone: Zone::AttW,
kind,
};
assert!(!event.commentary("Home").is_empty());
}
}
}