1use super::writer::Visitor;
2use crate::writer::PartialAcceptor;
3
4#[derive(Clone)]
5pub enum GameResult {
6 Finished { white_score: u32, black_score: u32 },
7 Ongoing,
8}
9
10impl From<&str> for GameResult {
11 fn from(value: &str) -> Self {
12 if value == "*" {
13 return Self::Ongoing;
14 }
15
16 let vec = value.split("-").collect::<Vec<&str>>();
17 if vec.len() != 2 {
18 return Self::Ongoing;
19 }
20
21 let white_score = if let Ok(val) = vec[0].parse::<u32>() {
22 val
23 } else {
24 return Self::Ongoing;
25 };
26 let black_score = if let Ok(val) = vec[1].parse::<u32>() {
27 val
28 } else {
29 return Self::Ongoing;
30 };
31
32 Self::Finished {
33 white_score,
34 black_score,
35 }
36 }
37}
38
39impl std::fmt::Display for GameResult {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 GameResult::Finished {
43 white_score,
44 black_score,
45 } => write!(f, "{}-{}", white_score, black_score),
46 GameResult::Ongoing => write!(f, "*"),
47 }
48 }
49}
50
51#[derive(Clone)]
52pub struct Header {
53 pub event: Option<String>,
54 pub site: Option<String>,
55 pub date: Option<String>,
56 pub round: Option<String>,
57 pub white: Option<String>,
58 pub black: Option<String>,
59 pub result: GameResult,
60}
61
62impl Default for Header {
63 fn default() -> Self {
64 Self {
65 event: None,
66 site: None,
67 date: None,
68 round: None,
69 white: None,
70 black: None,
71 result: GameResult::Ongoing,
72 }
73 }
74}
75
76fn parse_header_value(value: &str) -> Option<String> {
77 match value {
78 "?" => None,
79 "??" => None,
80 _ => Some(value.to_string()),
81 }
82}
83
84fn parse_header_date_value(value: &str) -> Option<String> {
85 match value {
86 "?" => None,
87 "??" => None,
88 "????.??.??" => None,
89 _ => Some(value.to_string()),
90 }
91}
92
93fn serialize_header_value(value: &Option<String>, default_str: &str) -> String {
94 value.clone().unwrap_or(default_str.to_string())
95}
96
97impl Header {
98 pub fn parse(&mut self, key: &str, value: &str) -> bool {
99 match key {
100 "Event" => self.event = parse_header_value(value),
101 "Site" => self.site = parse_header_value(value),
102 "Date" => self.date = parse_header_date_value(value),
103 "Round" => self.round = parse_header_value(value),
104 "White" => self.white = parse_header_value(value),
105 "Black" => self.black = parse_header_value(value),
106 "Result" => self.result = GameResult::from(value),
107 _ => return false,
108 }
109
110 true
111 }
112}
113
114impl PartialAcceptor for Header {
115 fn accept<V: Visitor>(&self, visitor: &mut V) {
116 visitor.visit_header("Event", &serialize_header_value(&self.event, "?"));
117 visitor.visit_header("Site", &serialize_header_value(&self.site, "?"));
118 visitor.visit_header("Date", &serialize_header_value(&self.date, "????.??.??"));
119 visitor.visit_header("Round", &serialize_header_value(&self.round, "?"));
120 visitor.visit_header("White", &serialize_header_value(&self.white, "?"));
121 visitor.visit_header("Black", &serialize_header_value(&self.black, "?"));
122 visitor.visit_header("Result", self.result.to_string().as_str());
123 }
124}