1use core::{error, fmt, str::FromStr};
2
3pub use std::collections::BTreeSet as Set;
4
5pub use winnow::ModalResult;
6pub use winnow::Parser;
7
8mod bits;
9
10pub mod chessbase;
11pub mod fen;
12pub mod pgn;
13pub mod san;
14pub mod text;
15pub mod uci;
16
17#[doc(inline)]
18pub use bits::Bits;
19
20#[doc(inline)]
21pub use fen::Fen;
22impl StrFormat for Fen {}
23
24#[doc(inline)]
25pub use pgn::Pgn;
26impl StrFormat for Pgn {}
27
28#[doc(inline)]
29pub use san::San;
30impl StrFormat for San {}
31
32#[doc(inline)]
33pub use text::Text;
34impl StrFormat for Text {}
35
36pub type ByteInput<'a> = &'a [u8];
38
39pub type StrInput<'a> = &'a str;
41
42pub trait StrFormat: fmt::Display + FromStr
44where
45 Self::Err: error::Error,
46{
47}
48
49pub(crate) mod prelude {
50 use std::io::{self, Read};
51
52 use super::ByteInput as Input;
53
54 pub use winnow::{
55 ModalResult,
56 Parser,
57 ascii::{dec_uint, multispace0, multispace1, space0, space1},
58 binary::{be_u16, be_u24, be_u32, i8, le_i32, le_u16, le_u24, le_u32, u8},
59 combinator::{
60 alt, backtrack_err, cut_err, delimited, opt, preceded, repeat, separated,
61 separated_pair, seq, terminated,
62 },
63 error::{ContextError, ErrMode, StrContext},
64 token::{any, none_of, one_of, take, take_till, take_while},
66 };
67
68 pub type ContextMode = ErrMode<ContextError>;
69
70 pub fn backtrack() -> ContextMode {
71 ErrMode::Backtrack(ContextError::new())
72 }
73
74 pub trait Framed {
75 const LEN: usize;
76
77 fn total(&self) -> usize;
78 }
79
80 pub enum FrameError<E> {
81 Error(E),
82 Io(io::Error),
83 }
84
85 pub fn framed<Header, T, H, P, F, E>(
86 input: &mut impl Read,
87 data: &mut Vec<u8>,
88 mut header: H,
89 parser: P,
90 ) -> core::result::Result<T, FrameError<E>>
91 where
92 Header: Framed,
93 H: for<'a> Parser<Input<'a>, Header, E>,
94 P: FnOnce(Header) -> F,
95 F: for<'a> Parser<Input<'a>, T, E>,
96 {
97 data.resize(Header::LEN, 0);
98 input.read_exact(data).map_err(FrameError::Io)?;
99 let mut prefix = data.as_slice();
100 let header = header.parse_next(&mut prefix).map_err(FrameError::Error)?;
101 data.resize(header.total(), 0);
102 input.read_exact(&mut data[Header::LEN..]).map_err(FrameError::Io)?;
103 let mut body = &data[Header::LEN..];
104 parser(header).parse_next(&mut body).map_err(FrameError::Error)
105 }
106}
107
108#[cfg(test)]
109pub mod twic {
110 pub fn data(issue: u16, suffix: &str) -> Vec<u8> {
111 let path = path(issue, suffix);
112 let error = |error| panic!("cannot read TWIC fixture {}: {error}", path.display());
113 std::fs::read(&path).unwrap_or_else(error)
114 }
115
116 pub fn path(issue: u16, suffix: &str) -> std::path::PathBuf {
117 std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(format!("twic/twic{issue}.{suffix}"))
118 }
119}