1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use std::convert::TryInto;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

const HEADER_LENGTH: usize = 16;

pub(crate) fn parse_header(header: &[u8]) -> Result<Header, HeaderError> {
    if header.len() != HEADER_LENGTH {
        return Err(HeaderError::InvalidHeader);
    }
    let century = *header.get(0).unwrap();
    let year = *header.get(1).unwrap();
    let month = *header.get(2).unwrap();
    let day = *header.get(3).unwrap();
    let file_creation_date = FileCreationDate {
        century,
        year,
        month,
        day,
    };

    let n1 = u32::from_le_bytes(header[4..8].try_into().unwrap());
    let n2 = u16::from_le_bytes(header[8..10].try_into().unwrap());

    let year_of_games = u16::from_le_bytes(header[10..12].try_into().unwrap());

    let board_size = header.get(12).unwrap().to_le();
    let board_size = match board_size {
        0 | 8 => BoardSize::EightSquared,
        10 => BoardSize::TenSquared,
        _ => return Err(HeaderError::UnsupportedBoardSize),
    };

    let is_solitaire = *header.get(13).unwrap();
    let is_solitaire = match is_solitaire {
        0 => false,
        1 => true,
        _ => return Err(HeaderError::InvalidP2Record),
    };
    let p3 = *header.get(14).unwrap();

    Ok(Header {
        file_creation_date,
        n1,
        n2,
        year_of_games,
        board_size,
        is_solitaire,
        p3,
    })
}

#[derive(Debug)]
pub enum HeaderError {
    InvalidHeader,
    UnsupportedBoardSize,
    InvalidN1Record,
    InvalidP2Record,
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct Header {
    pub file_creation_date: FileCreationDate,
    pub n1: u32,
    pub n2: u16,
    pub year_of_games: u16,
    pub board_size: BoardSize,
    pub is_solitaire: bool,
    pub p3: u8,
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub struct FileCreationDate {
    pub century: u8,
    pub year: u8,
    pub month: u8,
    pub day: u8,
}

#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug)]
pub enum BoardSize {
    EightSquared,
    TenSquared,
}

impl BoardSize {
    pub(crate) fn record_size_in_bytes(&self) -> usize {
        use BoardSize::*;
        match &self {
            EightSquared => 68,
            TenSquared => 104,
        }
    }
}