sacrifice/
header.rs

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}