chess/formats/chessbase/
player.rs1use crate::formats::{ByteInput as Input, prelude::*};
4
5use super::{
6 cstr,
7 table::{Header, header},
8};
9
10pub const SUFFIX: &str = "cbp";
12
13fn optional1(x: i32) -> Option<Option<usize>> {
18 match x {
19 -1 => Some(None),
20 0.. => Some(Some(x as usize)),
21 _ => None,
22 }
23}
24
25#[derive(Clone, Debug)]
26pub struct Player {
27 pub deleted: bool,
28 pub first_name: String,
29 pub last_name: String,
30 pub games: usize,
31 pub first_game: usize,
32}
33
34pub fn player(input: &mut Input<'_>) -> ModalResult<Player> {
35 let (left_child, deleted) =
36 le_i32
37 .verify_map(|x| {
38 if x == -999 { Some((None, true)) } else { optional1(x).map(|x| (x, false)) }
39 })
40 .parse_next(input)?;
41 let (right_child, next_deleted) = le_i32
42 .verify_map(|x| {
43 if deleted { optional1(x).map(|x| (None, x)) } else { optional1(x).map(|x| (x, None)) }
44 })
45 .parse_next(input)?;
46 let _right_height_minus_left_height =
47 i8.verify(|height| !deleted || *height == 0).parse_next(input)?;
48 let last_name = take(30u8).map(cstr).parse_next(input)?;
49 let first_name = take(20u8).map(cstr).parse_next(input)?;
50 let games = le_u32.parse_next(input)? as usize;
51 let first_game = le_u32.parse_next(input)? as usize;
52
53 let _ = (left_child, right_child, next_deleted);
54
55 Ok(Player { deleted, first_name, last_name, games, first_game })
56}
57
58#[derive(Clone, Debug)]
59pub struct Players {
60 pub header: Header,
61 pub players: Vec<Player>,
62}
63
64pub fn players(input: &mut Input<'_>) -> ModalResult<Players> {
65 let header = header.parse_next(input)?;
66 let players: Vec<Player> = repeat(..=header.len, player).parse_next(input)?;
67 Ok(Players { header, players })
68}
69
70#[cfg(test)]
71mod tests {
72 use hex_literal::hex;
73
74 use crate::formats::twic;
75
76 use super::*;
77
78 #[test]
79 #[ignore = "exploratory output from the external TWIC fixture"]
80 fn twic_players() {
81 let input = twic::data(1616, "cbp");
82 let header = header.parse_next(&mut input.as_slice()).expect("can parse CBP header");
83 println!("{header:?}");
84 let players = players.parse(input.as_slice()).map_err(drop).expect("can parse CBP file");
85 println!("{:?}", players.header);
86 for player in players.players.iter().take(3) {
87 println!("{player:?}");
88 }
89 for player in players.players.iter().rev().take(3) {
90 println!("{player:?}");
91 }
92 }
93
94 #[test]
95 fn player() {
96 const INPUT: [u8; 67] = hex!(
97 "ffffffffffffffff0041616761617264
98 00000000360038002500000000000080
99 000000009e86004a00004d003a007000
100 6d0000000000000000000005000000f0
101 0b0000"
102 );
103
104 let player = super::player.parse(INPUT.as_slice()).expect("can parse CBP player");
105 assert!(!player.deleted);
106 assert_eq!(player.first_name, "J");
107 assert_eq!(player.last_name, "Aagaard");
108 assert_eq!(player.games, 5);
109 assert_eq!(player.first_game, 3056);
110 }
111}