vampirc_uci/
parser.rs

1//! The `parser` module contains the `parse` method that performs the parsing of UCI messages into their respective
2//! `UciMessage` variants.
3//!
4//! Behind the scenes, it uses the [PEST parser](https://github.com/pest-parser/pest). The corresponding PEG grammar is
5//! available [here](https://github.com/vampirc/vampirc-uci/blob/master/res/uci.pest).
6
7#[cfg(feature = "chess")]
8use std::fmt::Error as FmtError;
9use std::str::FromStr;
10
11use chrono::Duration;
12use pest::error::Error;
13use pest::iterators::Pair;
14use pest::Parser;
15
16#[cfg(feature = "chess")]
17use crate::chess::{ChessMove, Piece, Square};
18use crate::uci::ProtectionState;
19use crate::uci::{
20    MessageList, UciFen, UciInfoAttribute, UciMessage, UciSearchControl, UciTimeControl,
21};
22#[cfg(not(feature = "chess"))]
23use crate::uci::{UciMove, UciPiece, UciSquare};
24use crate::UciOptionConfig;
25
26#[derive(Parser)]
27#[grammar = "../res/uci.pest"]
28struct UciParser;
29
30/// Parses the specified `&str s` into a list of `UciMessage`s. Please note that this method will return an `Error` if
31/// any of the input violates the grammar rules.
32///
33/// The UCI messages are separated by a newline character, as per the UCI protocol specification.
34///
35/// This method differs from the `parse(..)` method in the fact that any unrecognized tokens/messages will result in
36/// an error being returned.
37///
38/// # Examples
39///
40/// ```
41/// use vampirc_uci::UciMessage;
42/// use vampirc_uci::parse_strict;
43///
44/// let messages = parse_strict("position startpos\ngo ponder searchmoves e2e4 d2d4\n").unwrap();
45/// assert_eq!(messages.len(), 2);
46///
47/// ```
48pub fn parse_strict(s: &str) -> Result<MessageList, Error<Rule>> {
49    let mut ml = MessageList::new();
50    do_parse_uci(s, Rule::commands, Some(&mut ml))?;
51
52    Ok(ml)
53}
54
55/// Parses the specified `&str s` into a list of `UciMessage`s. Please note that this method will ignore any
56/// unrecognized messages, which is in-line with the recommendations of the UCI protocol specification.
57///
58/// The UCI messages are separated by a newline character, as per the UCI protocol specification.
59///
60/// This method differs from the `parse_strict(..)` method in the fact that any unrecognized tokens/messages will
61/// simply be ignored.
62///
63/// # Examples
64///
65/// ```
66/// use vampirc_uci::{UciMessage, parse};
67///
68/// let messages = parse("position startpos\n  unknown message that will be ignored  \ngo infinite\n");
69/// assert_eq!(messages.len(), 2);
70///
71/// ```
72pub fn parse(s: &str) -> MessageList {
73    let mut ml = MessageList::new();
74    do_parse_uci(s, Rule::commands_ignore_unknown, Some(&mut ml)).unwrap();
75
76    ml
77}
78
79/// This is like `parse`, except that it returns a `UciMessage::UnknownMessage` variant if it does not recognize the
80/// message.
81///
82/// /// # Examples
83///
84/// ```
85/// use vampirc_uci::{UciMessage, parse_with_unknown};
86///
87/// let messages = parse_with_unknown("not really a message");
88/// assert_eq!(messages.len(), 1);
89/// ```
90pub fn parse_with_unknown(s: &str) -> MessageList {
91    let mut ml = MessageList::new();
92    let parse_att = do_parse_uci(s, Rule::commands_with_unknown, Some(&mut ml));
93
94    if let Err(e) = parse_att {
95        let m = UciMessage::Unknown(s.trim_end().to_owned(), Some(e));
96        return vec![m];
97    }
98
99    ml
100}
101
102/// Parses and returns a single message, with or without a terminating newline. Usually used
103/// in a loop that reads a single line from an input stream, such as the stdin. Note that if the
104/// message is unrecognizable to the parser, a `UciMessage::UnknownMessage` variant is returned.
105///
106/// Only the first command in the `s` parameter will be returned, if there are more than one in
107/// that string.
108///
109/// /// # Examples
110///
111/// ```
112/// use std::io::{self, BufRead};
113/// use vampirc_uci::{UciMessage, parse_one};
114///
115/// for line in io::stdin().lock().lines() {
116///         let msg: UciMessage = parse_one(&line.unwrap());
117///         println!("Received message: {}", msg);
118///     }
119/// ```
120pub fn parse_one(s: &str) -> UciMessage {
121    let r = do_parse_uci(s, Rule::single_message_per_line, None);
122
123    if let Err(e) = r {
124        let m = UciMessage::Unknown(s.trim_end().to_owned(), Some(e));
125        return m;
126    }
127
128    if let Some(m) = r.unwrap() {
129        return m;
130    }
131
132    return UciMessage::Unknown(String::new(), None);
133}
134
135fn do_parse_uci(
136    s: &str,
137    top_rule: Rule,
138    mut ml: Option<&mut MessageList>,
139) -> Result<Option<UciMessage>, Error<Rule>> {
140    let pairs = UciParser::parse(top_rule, s)?;
141
142    let mut single: Option<UciMessage> = None;
143
144    pairs
145        .map(|pair: Pair<_>| {
146            match pair.as_rule() {
147                Rule::uci => UciMessage::Uci,
148                Rule::debug => {
149                    for sp in pair.into_inner() {
150                        match sp.as_rule() {
151                            Rule::switch => {
152                                return UciMessage::Debug(
153                                    sp.as_span().as_str().eq_ignore_ascii_case("on"),
154                                );
155                            }
156                            _ => unreachable!(),
157                        }
158                    }
159                    UciMessage::Debug(false)
160                }
161                Rule::isready => UciMessage::IsReady,
162                Rule::setoption => {
163                    let mut name: String = String::default();
164                    let mut value: String = String::default();
165
166                    for sp in pair.into_inner() {
167                        match sp.as_rule() {
168                            Rule::option_internal => {
169                                for spi in sp.into_inner() {
170                                    match spi.as_rule() {
171                                        Rule::option_name => {
172                                            name = spi.as_span().as_str().trim().to_string();
173                                        }
174                                        Rule::option_value => {
175                                            value = spi.as_span().as_str().to_string();
176                                        }
177                                        _ => {}
178                                    }
179                                }
180                            }
181                            //                        Rule::value => { value = sp.as_span().as_str().to_string(); },
182                            _ => (),
183                        }
184                    }
185
186                    let val = if value != String::default() {
187                        Some(value)
188                    } else {
189                        None
190                    };
191                    UciMessage::SetOption { name, value: val }
192                }
193                Rule::register => {
194                    for sp in pair.into_inner() {
195                        match sp.as_rule() {
196                            Rule::register_later => {
197                                return UciMessage::register_later();
198                            }
199                            Rule::register_nc => {
200                                let mut name: &str = "";
201
202                                for spi in sp.into_inner() {
203                                    match spi.as_rule() {
204                                        Rule::register_name => {
205                                            name = spi.as_span().as_str();
206                                        }
207                                        Rule::register_code => {
208                                            return UciMessage::register_code(name, spi.as_str());
209                                        }
210                                        _ => (),
211                                    }
212                                }
213                            }
214                            _ => unreachable!(),
215                        }
216                    }
217
218                    unreachable!()
219                }
220                Rule::ucinewgame => UciMessage::UciNewGame,
221                Rule::stop => UciMessage::Stop,
222                Rule::ponderhit => UciMessage::PonderHit,
223                Rule::quit => UciMessage::Quit,
224                Rule::position => {
225                    let mut startpos = false;
226                    let mut fen: Option<UciFen> = None;
227                    #[cfg(not(feature = "chess"))] let mut moves: Vec<UciMove> = Default::default();
228                    #[cfg(feature = "chess")] let mut moves: Vec<ChessMove> = Default::default();
229
230                    for sp in pair.into_inner() {
231                        match sp.as_rule() {
232                            Rule::startpos => {
233                                startpos = true;
234                            }
235                            Rule::fen => fen = Some(UciFen::from(sp.as_span().as_str())),
236                            Rule::a_move => {
237                                moves.push(parse_a_move(sp));
238                            }
239                            _ => {}
240                        }
241                    }
242
243                    UciMessage::Position {
244                        startpos,
245                        fen,
246                        moves,
247                    }
248                }
249                Rule::go => {
250                    let mut time_control: Option<UciTimeControl> = None;
251                    let mut tl = false;
252                    let mut wtime: Option<i64> = None;
253                    let mut btime: Option<i64> = None;
254                    let mut winc: Option<i64> = None;
255                    let mut binc: Option<i64> = None;
256                    let mut moves_to_go: Option<u8> = None;
257
258                    let mut search: UciSearchControl = UciSearchControl::default();
259
260                    for sp in pair.into_inner() {
261                        match sp.as_rule() {
262                            Rule::go_empty => {}
263                            Rule::go_full => {
264                                for sp_full in sp.into_inner() {
265                                    match sp_full.as_rule() {
266                                        Rule::go_time => {
267                                            for spi in sp_full.into_inner() {
268                                                match spi.as_rule() {
269                                                    Rule::go_ponder => {
270                                                        time_control = Some(UciTimeControl::Ponder);
271                                                    }
272                                                    Rule::go_infinite => {
273                                                        time_control = Some(UciTimeControl::Infinite);
274                                                    }
275                                                    Rule::go_movetime => {
276                                                        time_control = Some(UciTimeControl::MoveTime(
277                                                            Duration::milliseconds(parse_milliseconds(spi)),
278                                                        ));
279                                                    }
280                                                    Rule::go_timeleft => {
281                                                        if !tl {
282                                                            tl = true;
283                                                        }
284
285                                                        for sspi in spi.into_inner() {
286                                                            match sspi.as_rule() {
287                                                                Rule::wtime => {
288                                                                    wtime = Some(parse_milliseconds(sspi));
289                                                                }
290                                                                Rule::btime => {
291                                                                    btime = Some(parse_milliseconds(sspi));
292                                                                }
293                                                                Rule::winc => {
294                                                                    winc = Some(parse_milliseconds(sspi));
295                                                                }
296                                                                Rule::binc => {
297                                                                    binc = Some(parse_milliseconds(sspi));
298                                                                }
299                                                                Rule::movestogo => {
300                                                                    moves_to_go =
301                                                                        Some(parse_u8(sspi, Rule::digits3));
302                                                                }
303                                                                _ => {}
304                                                            };
305                                                        }
306                                                    }
307
308                                                    _ => {}
309                                                }
310                                            }
311                                        }
312                                        Rule::go_search => {
313                                            for spi in sp_full.into_inner() {
314                                                match spi.as_rule() {
315                                                    Rule::depth => {
316                                                        search.depth = Some(parse_u8(spi, Rule::digits3));
317                                                    }
318                                                    Rule::mate => {
319                                                        search.mate = Some(parse_u8(spi, Rule::digits3))
320                                                    }
321                                                    Rule::nodes => {
322                                                        search.nodes = Some(parse_u64(spi, Rule::digits12))
323                                                    }
324                                                    Rule::searchmoves => {
325                                                        for mt in spi.into_inner() {
326                                                            search.search_moves.push(parse_a_move(mt));
327                                                        }
328                                                    }
329                                                    _ => {}
330                                                }
331                                            }
332                                        }
333                                        _ => unreachable!()
334                                    }
335                                }
336                            }
337                            _ => unreachable!(),
338                        }
339                    }
340
341                    if tl {
342                        time_control = Some(UciTimeControl::TimeLeft {
343                            white_time: wtime.map(|millis| Duration::milliseconds(millis)),
344                            black_time: btime.map(|millis| Duration::milliseconds(millis)),
345                            white_increment: winc.map(|millis| Duration::milliseconds(millis)),
346                            black_increment: binc.map(|millis| Duration::milliseconds(millis)),
347                            moves_to_go,
348                        });
349                    }
350
351                    let search_control: Option<UciSearchControl>;
352                    if search.is_empty() {
353                        search_control = None
354                    } else {
355                        search_control = Some(search);
356                    }
357
358                    UciMessage::Go {
359                        time_control,
360                        search_control,
361                    }
362                }
363                Rule::id => {
364                    for sp in pair.into_inner() {
365                        let id_rule: Rule = sp.as_rule();
366                        match id_rule {
367                            Rule::id_name | Rule::id_author => {
368                                return parse_id_text(sp, id_rule);
369                            }
370                            _ => {}
371                        }
372                    }
373
374                    unreachable!()
375                }
376                Rule::uciok => UciMessage::UciOk,
377                Rule::readyok => UciMessage::ReadyOk,
378                Rule::bestmove => {
379                    #[cfg(not(feature = "chess"))] let mut bm: Option<UciMove> = None;
380                    #[cfg(not(feature = "chess"))] let mut ponder: Option<UciMove> = None;
381                    #[cfg(feature = "chess")] let mut bm: Option<ChessMove> = None;
382                    #[cfg(feature = "chess")] let mut ponder: Option<ChessMove> = None;
383                    for sp in pair.into_inner() {
384                        match sp.as_rule() {
385                            Rule::a_move => {
386                                bm = Some(parse_a_move(sp));
387                            }
388                            Rule::bestmove_ponder => {
389                                for ssp in sp.into_inner() {
390                                    match ssp.as_rule() {
391                                        Rule::a_move => ponder = Some(parse_a_move(ssp)),
392                                        _ => {}
393                                    }
394                                }
395                            }
396                            _ => {}
397                        }
398                    }
399
400                    UciMessage::BestMove {
401                        best_move: bm.unwrap(),
402                        ponder,
403                    }
404                }
405                Rule::copyprotection | Rule::registration => {
406                    let mut ps: Option<ProtectionState> = None;
407                    let pc = pair.clone();
408                    for sp in pair.into_inner() {
409                        match sp.as_rule() {
410                            Rule::protection_checking => ps = Some(ProtectionState::Checking),
411                            Rule::protection_ok => ps = Some(ProtectionState::Ok),
412                            Rule::protection_error => ps = Some(ProtectionState::Error),
413                            _ => {}
414                        }
415                    }
416
417                    if pc.as_rule() == Rule::copyprotection {
418                        UciMessage::CopyProtection(ps.unwrap())
419                    } else {
420                        UciMessage::Registration(ps.unwrap())
421                    }
422                }
423                Rule::option => {
424                    let mut name: Option<&str> = None;
425                    let mut opt_default: Option<&str> = None;
426                    let mut opt_min: Option<i64> = None;
427                    let mut opt_max: Option<i64> = None;
428                    let mut opt_var: Vec<String> = Vec::default();
429                    let mut type_pair: Option<Pair<Rule>> = None;
430
431                    for sp in pair.into_inner() {
432                        match sp.as_rule() {
433                            Rule::option_name2 => {
434                                name = Some(sp.as_span().as_str());
435                            }
436                            Rule::option_type => {
437                                for spi in sp.into_inner() {
438                                    match spi.as_rule() {
439                                        Rule::option_check
440                                        | Rule::option_spin
441                                        | Rule::option_combo
442                                        | Rule::option_string
443                                        | Rule::option_button => {
444                                            type_pair = Some(spi);
445                                        }
446                                        _ => {}
447                                    }
448                                }
449                            }
450                            Rule::option_default => {
451                                opt_default = Some(sp.as_span().as_str());
452                            }
453                            Rule::option_min => {
454                                opt_min = Some(parse_i64(sp, Rule::i64));
455                            }
456                            Rule::option_max => {
457                                opt_max = Some(parse_i64(sp, Rule::i64));
458                            }
459                            Rule::option_var => {
460                                opt_var.push(String::from(sp.as_span().as_str()));
461                            }
462                            _ => unreachable!(),
463                        }
464                    }
465
466                    let uoc: UciOptionConfig = match type_pair.unwrap().as_rule() {
467                        Rule::option_check => UciOptionConfig::Check {
468                            name: String::from(name.unwrap()),
469                            default: if let Some(def) = opt_default {
470                                match def.to_lowercase().as_str() {
471                                    "true" => Some(true),
472                                    "false" => Some(false),
473                                    _ => None,
474                                }
475                            } else {
476                                None
477                            },
478                        },
479                        Rule::option_spin => UciOptionConfig::Spin {
480                            name: String::from(name.unwrap()),
481                            default: if let Some(def) = opt_default {
482                                if let Ok(def_i64) = str::parse::<i64>(def) {
483                                    Some(def_i64)
484                                } else {
485                                    None
486                                }
487                            } else {
488                                None
489                            },
490                            min: if let Some(min1) = opt_min {
491                                Some(min1)
492                            } else {
493                                None
494                            },
495                            max: if let Some(max1) = opt_max {
496                                Some(max1)
497                            } else {
498                                None
499                            },
500                        },
501                        Rule::option_combo => UciOptionConfig::Combo {
502                            name: String::from(name.unwrap()),
503                            default: if let Some(def) = opt_default {
504                                if def.eq_ignore_ascii_case("<empty>") {
505                                    Some(String::from(""))
506                                } else {
507                                    Some(String::from(def))
508                                }
509                            } else {
510                                None
511                            },
512                            var: opt_var,
513                        },
514                        Rule::option_string => UciOptionConfig::String {
515                            name: String::from(name.unwrap()),
516                            default: if let Some(def) = opt_default {
517                                if def.eq_ignore_ascii_case("<empty>") {
518                                    Some(String::from(""))
519                                } else {
520                                    Some(String::from(def))
521                                }
522                            } else {
523                                None
524                            },
525                        },
526                        Rule::option_button => UciOptionConfig::Button {
527                            name: String::from(name.unwrap()),
528                        },
529                        _ => unreachable!(),
530                    };
531
532                    UciMessage::Option(uoc)
533                }
534                Rule::info => {
535                    let mut info_attr: Vec<UciInfoAttribute> = vec![];
536
537                    for sp in pair.into_inner() {
538                        match sp.as_rule() {
539                            Rule::info_attribute => {
540                                for spi in sp.into_inner() {
541                                    match spi.as_rule() {
542                                        Rule::info_depth => {
543                                            let info_depth = UciInfoAttribute::Depth(parse_u8(
544                                                spi,
545                                                Rule::digits3,
546                                            ));
547                                            info_attr.push(info_depth);
548                                            break;
549                                        }
550                                        Rule::info_seldepth => {
551                                            let info_depth = UciInfoAttribute::SelDepth(parse_u8(
552                                                spi,
553                                                Rule::digits3,
554                                            ));
555                                            info_attr.push(info_depth);
556                                            break;
557                                        }
558                                        Rule::info_time => {
559                                            let info_time = UciInfoAttribute::Time(Duration::milliseconds(parse_i64(
560                                                spi,
561                                                Rule::digits12,
562                                            )));
563                                            info_attr.push(info_time);
564                                            break;
565                                        }
566                                        Rule::info_nodes => {
567                                            let info_nodes = UciInfoAttribute::Nodes(parse_u64(
568                                                spi,
569                                                Rule::digits12,
570                                            ));
571                                            info_attr.push(info_nodes);
572                                            break;
573                                        }
574                                        Rule::info_currmovenum => {
575                                            let an_info = UciInfoAttribute::CurrMoveNum(parse_u64(
576                                                spi,
577                                                Rule::digits12,
578                                            )
579                                                as u16);
580                                            info_attr.push(an_info);
581                                            break;
582                                        }
583                                        Rule::info_hashfull => {
584                                            let an_info = UciInfoAttribute::HashFull(parse_u64(
585                                                spi,
586                                                Rule::digits12,
587                                            )
588                                                as u16);
589                                            info_attr.push(an_info);
590                                            break;
591                                        }
592                                        Rule::info_nps => {
593                                            let an_info = UciInfoAttribute::Nps(parse_u64(
594                                                spi,
595                                                Rule::digits12,
596                                            ));
597                                            info_attr.push(an_info);
598                                            break;
599                                        }
600                                        Rule::info_tbhits => {
601                                            let an_info = UciInfoAttribute::TbHits(parse_u64(
602                                                spi,
603                                                Rule::digits12,
604                                            ));
605                                            info_attr.push(an_info);
606                                            break;
607                                        }
608                                        Rule::info_sbhits => {
609                                            let an_info = UciInfoAttribute::SbHits(parse_u64(
610                                                spi,
611                                                Rule::digits12,
612                                            ));
613                                            info_attr.push(an_info);
614                                            break;
615                                        }
616                                        Rule::info_cpuload => {
617                                            let an_info = UciInfoAttribute::CpuLoad(parse_u64(
618                                                spi,
619                                                Rule::digits12,
620                                            )
621                                                as u16);
622                                            info_attr.push(an_info);
623                                            break;
624                                        }
625                                        Rule::info_multipv => {
626                                            let an_info = UciInfoAttribute::MultiPv(parse_u64(
627                                                spi,
628                                                Rule::digits12,
629                                            )
630                                                as u16);
631                                            info_attr.push(an_info);
632                                            break;
633                                        }
634                                        Rule::info_pv => {
635                                            #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
636                                            #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
637                                            for spii in spi.into_inner() {
638                                                match spii.as_rule() {
639                                                    Rule::a_move => {
640                                                        let a_move = parse_a_move(spii);
641                                                        mv.push(a_move);
642                                                    }
643                                                    _ => {}
644                                                }
645                                            }
646                                            info_attr.push(UciInfoAttribute::Pv(mv));
647                                            break;
648                                        }
649                                        Rule::info_refutation => {
650                                            #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
651                                            #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
652                                            for spii in spi.into_inner() {
653                                                match spii.as_rule() {
654                                                    Rule::a_move => {
655                                                        let a_move = parse_a_move(spii);
656                                                        mv.push(a_move);
657                                                    }
658                                                    _ => {}
659                                                }
660                                            }
661                                            info_attr.push(UciInfoAttribute::Refutation(mv));
662                                            break;
663                                        }
664                                        Rule::info_currline => {
665                                            #[cfg(not(feature = "chess"))] let mut mv: Vec<UciMove> = vec![];
666                                            #[cfg(feature = "chess")] let mut mv: Vec<ChessMove> = vec![];
667                                            let mut cpu_nr: Option<u16> = None;
668                                            for spii in spi.into_inner() {
669                                                match spii.as_rule() {
670                                                    Rule::a_move => {
671                                                        let a_move = parse_a_move(spii);
672                                                        mv.push(a_move);
673                                                    }
674                                                    Rule::info_cpunr => {
675                                                        cpu_nr =
676                                                            Some(parse_u64(spii, Rule::digits3)
677                                                                as u16);
678                                                    }
679                                                    _ => {}
680                                                }
681                                            }
682                                            info_attr.push(UciInfoAttribute::CurrLine {
683                                                cpu_nr,
684                                                line: mv,
685                                            });
686                                            break;
687                                        }
688                                        Rule::info_string => {
689                                            for spii in spi.into_inner() {
690                                                match spii.as_rule() {
691                                                    Rule::info_string_string => {
692                                                        let an_info = UciInfoAttribute::String(
693                                                            spii.as_span().as_str().to_owned(),
694                                                        );
695                                                        info_attr.push(an_info);
696                                                        break;
697                                                    }
698                                                    _ => {}
699                                                }
700                                            }
701                                            break;
702                                        }
703                                        Rule::info_currmove => {
704                                            for spii in spi.into_inner() {
705                                                match spii.as_rule() {
706                                                    Rule::a_move => {
707                                                        let an_info = UciInfoAttribute::CurrMove(
708                                                            parse_a_move(spii),
709                                                        );
710                                                        info_attr.push(an_info);
711                                                        break;
712                                                    }
713                                                    _ => {}
714                                                }
715                                            }
716                                            break;
717                                        }
718                                        Rule::info_score => {
719                                            let mut cp: Option<i32> = None;
720                                            let mut mate: Option<i8> = None;
721                                            let mut lb: Option<bool> = None;
722                                            let mut ub: Option<bool> = None;
723
724                                            for spii in spi.into_inner() {
725                                                match spii.as_rule() {
726                                                    Rule::info_cp => cp = Some(parse_i64(spii, Rule::i64) as i32),
727                                                    Rule::info_mate => mate = Some(parse_i64(spii, Rule::i64) as i8),
728                                                    Rule::info_lowerbound => lb = Some(true),
729                                                    Rule::info_upperbound => ub = Some(true),
730                                                    _ => {}
731                                                }
732                                            }
733
734                                            info_attr.push(UciInfoAttribute::Score {
735                                                cp,
736                                                mate,
737                                                lower_bound: lb,
738                                                upper_bound: ub,
739                                            });
740                                        }
741                                        Rule::info_any => {
742                                            let mut s: Option<String> = None;
743                                            let mut t: Option<String> = None;
744
745                                            for spii in spi.into_inner() {
746                                                match spii.as_rule() {
747                                                    Rule::token => {
748                                                        t = Some(
749                                                            spii.as_span().as_str().to_owned(),
750                                                        );
751                                                    }
752                                                    Rule::info_string_string => {
753                                                        s = Some(
754                                                            spii.as_span().as_str().to_owned(),
755                                                        );
756                                                    }
757                                                    _ => {}
758                                                }
759                                            }
760                                            let an_info =
761                                                UciInfoAttribute::Any(t.unwrap(), s.unwrap());
762                                            info_attr.push(an_info);
763                                            break;
764                                        }
765                                        _ => unreachable!(),
766                                    }
767                                }
768                            }
769                            _ => unreachable!(),
770                        }
771                    }
772
773                    UciMessage::Info(info_attr)
774                }
775                Rule::something_produced => {
776                    UciMessage::Unknown(pair.as_span().as_str().to_string(), None)
777                }
778                Rule::something_produced_nl => {
779                    UciMessage::Unknown(pair.as_span().as_str().trim_end().to_string(), None)
780                }
781
782                _ => unreachable!(),
783            }
784        })
785        .for_each(|msg| {
786            if let Some(a_ml) = &mut ml {
787                (*a_ml).push(msg);
788            } else {
789                single = Some(msg);
790            }
791        });
792
793    Ok(single)
794}
795
796fn parse_id_text(id_pair: Pair<Rule>, rule: Rule) -> UciMessage {
797    for sp in id_pair.into_inner() {
798        match sp.as_rule() {
799            Rule::id_text => {
800                let text = sp.as_span().as_str();
801                match rule {
802                    Rule::id_name => {
803                        return UciMessage::Id {
804                            name: Some(String::from(text)),
805                            author: None,
806                        };
807                    }
808                    Rule::id_author => {
809                        return UciMessage::Id {
810                            author: Some(String::from(text)),
811                            name: None,
812                        };
813                    }
814                    _ => unreachable!(),
815                }
816            }
817            _ => {}
818        }
819    }
820
821    unreachable!();
822}
823
824#[cfg(not(feature = "chess"))]
825fn parse_square(sq_pair: Pair<Rule>) -> UciSquare {
826    let mut file: char = '\0';
827    let mut rank: u8 = 0;
828
829    match sq_pair.as_rule() {
830        Rule::square => {
831            for sp in sq_pair.into_inner() {
832                match sp.as_rule() {
833                    Rule::file => {
834                        file = sp.as_span().as_str().chars().into_iter().next().unwrap();
835                    }
836                    Rule::rank => {
837                        rank = str::parse(sp.as_span().as_str()).unwrap();
838                    }
839                    _ => unreachable!(),
840                }
841            }
842        }
843        _ => unreachable!(),
844    }
845
846    UciSquare::from(file, rank)
847}
848
849#[cfg(feature = "chess")]
850fn parse_square(sq_pair: Pair<Rule>) -> Square {
851    let mut file: char = '\0';
852    let mut rank: u8 = 0;
853
854    match sq_pair.as_rule() {
855        Rule::square => {
856            for sp in sq_pair.into_inner() {
857                match sp.as_rule() {
858                    Rule::file => {
859                        file = sp.as_span().as_str().chars().into_iter().next().unwrap();
860                    }
861                    Rule::rank => {
862                        rank = str::parse(sp.as_span().as_str()).unwrap();
863                    }
864                    _ => unreachable!(),
865                }
866            }
867        }
868        _ => unreachable!(),
869    }
870
871    Square::from_str(format!("{}{}", file.to_string(), rank.to_string()).as_str()).unwrap()
872}
873
874fn parse_milliseconds(pair: Pair<Rule>) -> i64 {
875    for sp in pair.into_inner() {
876        match sp.as_rule() {
877            Rule::milliseconds => {
878                return str::parse::<i64>(sp.as_span().as_str()).unwrap();
879            }
880            _ => {}
881        }
882    }
883
884    0
885}
886
887fn parse_u8(pair: Pair<Rule>, rule: Rule) -> u8 {
888    for sp in pair.into_inner() {
889        if sp.as_rule() == rule {
890            return str::parse::<u8>(sp.as_span().as_str()).unwrap();
891        }
892    }
893
894    0
895}
896
897fn parse_u64(pair: Pair<Rule>, rule: Rule) -> u64 {
898    for sp in pair.into_inner() {
899        if sp.as_rule() == rule {
900            return str::parse::<u64>(sp.as_span().as_str()).unwrap();
901        }
902    }
903
904    0
905}
906
907fn parse_i64(pair: Pair<Rule>, rule: Rule) -> i64 {
908    for sp in pair.into_inner() {
909        if sp.as_rule() == rule {
910            return str::parse::<i64>(sp.as_span().as_str()).unwrap();
911        }
912    }
913
914    0
915}
916
917#[cfg(not(feature = "chess"))]
918fn parse_a_move(sp: Pair<Rule>) -> UciMove {
919    let mut from_sq = UciSquare::default();
920    let mut to_sq = UciSquare::default();
921    let mut promotion: Option<UciPiece> = None;
922
923    for move_token in sp.into_inner() {
924        match move_token.as_rule() {
925            Rule::from_sq => {
926                from_sq = parse_square(move_token.into_inner().next().unwrap());
927            }
928            Rule::to_sq => {
929                to_sq = parse_square(move_token.into_inner().next().unwrap());
930            }
931            Rule::promotion => {
932                promotion = Some(UciPiece::from_str(move_token.as_span().as_str()).unwrap());
933            }
934            _ => unreachable!(),
935        }
936    }
937
938    UciMove {
939        from: from_sq,
940        to: to_sq,
941        promotion,
942    }
943}
944
945#[cfg(feature = "chess")]
946fn parse_a_move(sp: Pair<Rule>) -> ChessMove {
947    let mut from_sq = Square::default();
948    let mut to_sq = Square::default();
949    let mut promotion: Option<Piece> = None;
950
951    for move_token in sp.into_inner() {
952        match move_token.as_rule() {
953            Rule::from_sq => {
954                from_sq = parse_square(move_token.into_inner().next().unwrap());
955            }
956            Rule::to_sq => {
957                to_sq = parse_square(move_token.into_inner().next().unwrap());
958            }
959            Rule::promotion => {
960                promotion = Some(piece_from_str(move_token.as_span().as_str()).unwrap());
961            }
962            _ => unreachable!(),
963        }
964    }
965
966    ChessMove::new(from_sq, to_sq, promotion)
967}
968
969#[cfg(feature = "chess")]
970fn piece_from_str(s: &str) -> Result<Piece, FmtError> {
971    match s.to_ascii_lowercase().as_str() {
972        "n" => Ok(Piece::Knight),
973        "p" => Ok(Piece::Pawn),
974        "b" => Ok(Piece::Bishop),
975        "r" => Ok(Piece::Rook),
976        "k" => Ok(Piece::King),
977        "q" => Ok(Piece::Queen),
978        _ => Err(FmtError),
979    }
980}
981
982#[cfg(test)]
983mod tests {
984    use std::io::*;
985
986    use crate::uci::Serializable;
987
988    use super::*;
989
990    #[test]
991    fn test_uci() {
992        let ml = parse_strict("uci\r\nuci\r\n").unwrap();
993        assert_eq!(ml.len(), 2);
994        for mb in ml {
995            //let mbb = &(*mb);
996            assert_eq!(mb, UciMessage::Uci);
997        }
998    }
999
1000    #[test]
1001    fn test_debug_on() {
1002        let ml = parse_strict("debug    on\r\n").unwrap();
1003        assert_eq!(ml.len(), 1);
1004        assert_eq!(ml[0], UciMessage::Debug(true));
1005    }
1006
1007    #[test]
1008    fn test_debug_off() {
1009        let ml = parse_strict("debug off\n").unwrap();
1010        assert_eq!(ml.len(), 1);
1011        assert_eq!(ml[0], UciMessage::Debug(false));
1012    }
1013
1014    #[test]
1015    fn test_debugon() {
1016        parse_strict("debugon\r\n").expect_err("Should not parse 'debugon'");
1017    }
1018
1019    #[test]
1020    fn test_debug_wrong_param() {
1021        let ml = parse_strict("debug abc\r\n");
1022        assert_eq!(ml.is_err(), true);
1023    }
1024
1025    #[test]
1026    fn test_debug_cutoff() {
1027        parse_strict("debug    ontario\r\n").expect_err("Should not parse");
1028    }
1029
1030    #[test]
1031    fn test_isready() {
1032        let ml = parse_strict(" \tisready  \r\n").unwrap();
1033        assert_eq!(ml.len(), 1);
1034        assert_eq!(ml[0], UciMessage::IsReady);
1035    }
1036
1037    #[test]
1038    fn test_set_option_bool() {
1039        let ml = parse_strict("setoption name Nullmove value true\n").unwrap();
1040        assert_eq!(ml.len(), 1);
1041        let so = &ml[0];
1042
1043        match so {
1044            UciMessage::SetOption { name, value } => {
1045                assert_eq!(*name, String::from("Nullmove"));
1046                let val = value.clone();
1047                assert_eq!(val.is_some(), true);
1048                assert_eq!(val.unwrap().as_str(), String::from("true"));
1049                assert_eq!(so.as_bool().unwrap(), true);
1050            }
1051            _ => unreachable!(),
1052        }
1053    }
1054
1055    // setoption name Selectivity value 3\n
1056    #[test]
1057    fn test_set_option_int() {
1058        let ml = parse_strict("setoption name Selectivity is awesome value 3\n").unwrap();
1059        assert_eq!(ml.len(), 1);
1060        let so = &ml[0];
1061
1062        match so {
1063            UciMessage::SetOption { name, value } => {
1064                assert_eq!(*name, String::from("Selectivity is awesome"));
1065                let val = value.clone();
1066                assert_eq!(val.is_some(), true);
1067                assert_eq!(val.unwrap().as_str(), String::from("3"));
1068                assert_eq!(so.as_bool().is_none(), true);
1069                assert_eq!(so.as_i32().unwrap(), 3);
1070            }
1071            _ => unreachable!(),
1072        }
1073    }
1074
1075    // setoption name Clear Hash
1076    #[test]
1077    fn test_set_option_button() {
1078        let ml = parse_strict("setoption name Clear Hash\r\n").unwrap();
1079        assert_eq!(ml.len(), 1);
1080        let so = &ml[0];
1081
1082        match so {
1083            UciMessage::SetOption { name, value } => {
1084                assert_eq!(*name, String::from("Clear Hash"));
1085                let val = value.clone();
1086                assert_eq!(val.is_some(), false);
1087            }
1088            _ => unreachable!(),
1089        }
1090    }
1091
1092    #[test]
1093    fn test_set_option_str() {
1094        let ml =
1095            parse_strict("setoption name NalimovPath value c:\\chess\\tb\\4;c:\\chess\\tb\\5\n")
1096                .unwrap();
1097        assert_eq!(ml.len(), 1);
1098        let so = &ml[0];
1099
1100        match so {
1101            UciMessage::SetOption { name, value } => {
1102                assert_eq!(*name, String::from("NalimovPath"));
1103                let val = value.clone();
1104                assert_eq!(val.is_some(), true);
1105                assert_eq!(
1106                    val.unwrap().as_str(),
1107                    String::from("c:\\chess\\tb\\4;c:\\chess\\tb\\5")
1108                );
1109                assert_eq!(so.as_bool(), None);
1110            }
1111            _ => unreachable!(),
1112        }
1113    }
1114
1115    #[test]
1116    fn test_register_later() {
1117        let ml = parse_strict("REGISTER    lateR\r\n").unwrap();
1118        assert_eq!(ml.len(), 1);
1119        assert_eq!(ml[0], UciMessage::register_later());
1120    }
1121
1122    #[test]
1123    fn test_register_name_code() {
1124        let ml = parse_strict("register name Matija Kejžar code 4359874324\n").unwrap();
1125        assert_eq!(ml.len(), 1);
1126        assert_eq!(
1127            ml[0],
1128            UciMessage::register_code("Matija Kejžar", "4359874324")
1129        );
1130    }
1131
1132    #[test]
1133    fn test_register_invalid() {
1134        parse_strict("register name Matija Kejžar\n").expect_err("Parse error expected.");
1135    }
1136
1137    #[test]
1138    fn test_register_invalid2() {
1139        parse_strict("register code XX-344-00LP name Matija Kejžar\n")
1140            .expect_err("Parse error expected.");
1141    }
1142
1143    #[test]
1144    fn test_ucinewgame() {
1145        let ml = parse_strict(" ucinewGAME \r\n").unwrap();
1146        assert_eq!(ml.len(), 1);
1147        assert_eq!(ml[0], UciMessage::UciNewGame);
1148    }
1149
1150    #[test]
1151    fn test_stop() {
1152        let ml = parse_strict("stop\r\n").unwrap();
1153        assert_eq!(ml.len(), 1);
1154        assert_eq!(ml[0], UciMessage::Stop);
1155    }
1156
1157    #[test]
1158    fn test_stop_really_stop() {
1159        parse_strict("stopper\r\n").expect_err("Parse error expected for 'stopper'.");
1160    }
1161
1162    #[test]
1163    fn test_ponderhit() {
1164        let ml = parse_strict("PonderHit   \r\n").unwrap();
1165        assert_eq!(ml.len(), 1);
1166        assert_eq!(ml[0], UciMessage::PonderHit);
1167    }
1168
1169    #[test]
1170    fn test_quit() {
1171        let ml = parse_strict("QUIT\r\n").unwrap();
1172        assert_eq!(ml.len(), 1);
1173        assert_eq!(ml[0], UciMessage::Quit);
1174    }
1175
1176    #[test]
1177    fn test_position_startpos() {
1178        let ml = parse_strict("position startpos moves e2e4 e7e5\r\n").unwrap();
1179        assert_eq!(ml.len(), 1);
1180
1181        #[cfg(not(feature = "chess"))]
1182        {
1183            let m1 = UciMove {
1184                from: UciSquare { file: 'e', rank: 2 },
1185                to: UciSquare { file: 'e', rank: 4 },
1186                promotion: None,
1187            };
1188
1189            let m2 = UciMove {
1190                from: UciSquare { file: 'e', rank: 7 },
1191                to: UciSquare { file: 'e', rank: 5 },
1192                promotion: None,
1193            };
1194
1195            let pos = UciMessage::Position {
1196                startpos: true,
1197                fen: None,
1198                moves: vec![m1, m2],
1199            };
1200
1201            assert_eq!(ml[0], pos);
1202        }
1203
1204        #[cfg(feature = "chess")]
1205        {
1206            let m1 = ChessMove::new(Square::E2, Square::E4, None);
1207            let m2 = ChessMove::new(Square::E7, Square::E5, None);
1208
1209            let pos = UciMessage::Position {
1210                startpos: true,
1211                fen: None,
1212                moves: vec![m1, m2],
1213            };
1214
1215            assert_eq!(ml[0], pos);
1216        }
1217    }
1218
1219    #[test]
1220    fn test_position_startpos_as_fen() {
1221        let ml = parse_strict(
1222            "position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 moves d2d4\r\n",
1223        )
1224        .unwrap();
1225        assert_eq!(ml.len(), 1);
1226
1227        #[cfg(not(feature = "chess"))]
1228        let m1 = UciMove {
1229            from: UciSquare { file: 'd', rank: 2 },
1230            to: UciSquare { file: 'd', rank: 4 },
1231            promotion: None,
1232        };
1233
1234        #[cfg(feature = "chess")]
1235        let m1 = ChessMove::new(Square::D2, Square::D4, None);
1236
1237        let pos = UciMessage::Position {
1238            startpos: false,
1239            fen: Some(UciFen(String::from(
1240                "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
1241            ))),
1242            moves: vec![m1],
1243        };
1244
1245        assert_eq!(ml[0], pos);
1246    }
1247
1248    // 2k5/6PR/8/8/2b4P/8/6K1/8 w - -
1249    #[test]
1250    fn test_position_endgame() {
1251        let ml =
1252            parse_strict("position fen 2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53 moves g7g8q c4g8\r\n")
1253                .unwrap();
1254        assert_eq!(ml.len(), 1);
1255
1256        #[cfg(not(feature = "chess"))]
1257        let m1 = UciMove {
1258            from: UciSquare { file: 'g', rank: 7 },
1259            to: UciSquare { file: 'g', rank: 8 },
1260            promotion: Some(UciPiece::Queen),
1261        };
1262
1263        #[cfg(not(feature = "chess"))]
1264        let m2 = UciMove {
1265            from: UciSquare { file: 'c', rank: 4 },
1266            to: UciSquare { file: 'g', rank: 8 },
1267            promotion: None,
1268        };
1269
1270        #[cfg(feature = "chess")]
1271        let m1 = ChessMove::new(Square::G7, Square::G8, Some(Piece::Queen));
1272
1273        #[cfg(feature = "chess")]
1274        let m2 = ChessMove::new(Square::C4, Square::G8, None);
1275
1276        let pos = UciMessage::Position {
1277            startpos: false,
1278            fen: Some(UciFen(String::from("2k5/6PR/8/8/2b4P/8/6K1/8 w - - 0 53"))),
1279            moves: vec![m1, m2],
1280        };
1281
1282        assert_eq!(ml[0], pos);
1283    }
1284
1285    #[test]
1286    fn test_position_incorrect_fen() {
1287        parse_strict("position fen 2k50/6PR/8/8/2b4P/8/6K1/8 w - - 0 53 moves g7g8q c4g8\r\n")
1288            .expect_err("Parse should fail.");
1289    }
1290
1291    #[test]
1292    fn test_position_startpos_no_moves() {
1293        let ml = parse_strict("position   startpos\r\n").unwrap();
1294        assert_eq!(ml.len(), 1);
1295
1296        let pos = UciMessage::Position {
1297            startpos: true,
1298            fen: None,
1299            moves: vec![],
1300        };
1301
1302        assert_eq!(ml[0], pos);
1303    }
1304
1305    #[test]
1306    fn test_position_fen_no_moves() {
1307        let ml = parse_strict("position    fen 2k5/6PR/8/8/2b4P/8/6K1/8 w   - - 0 53\r\n").unwrap();
1308        assert_eq!(ml.len(), 1);
1309
1310        let pos = UciMessage::Position {
1311            startpos: false,
1312            fen: Some(UciFen(String::from(
1313                "2k5/6PR/8/8/2b4P/8/6K1/8 w   - - 0 53",
1314            ))),
1315            moves: vec![],
1316        };
1317
1318        assert_eq!(ml[0], pos);
1319    }
1320
1321    #[test]
1322    fn test_go_ponder() {
1323        let ml = parse_strict("go ponder\n").unwrap();
1324        assert_eq!(ml.len(), 1);
1325
1326        assert_eq!(ml[0], UciMessage::go_ponder());
1327    }
1328
1329    #[test]
1330    fn test_go_infinite() {
1331        let ml = parse_strict("go infinite\n").unwrap();
1332        assert_eq!(ml.len(), 1);
1333
1334        assert_eq!(ml[0], UciMessage::go_infinite());
1335    }
1336
1337    #[test]
1338    fn test_go_movetime() {
1339        let ml = parse_strict("go movetime  55055\n").unwrap();
1340        assert_eq!(ml.len(), 1);
1341
1342        assert_eq!(
1343            ml[0],
1344            UciMessage::go_movetime(Duration::milliseconds(55055))
1345        );
1346    }
1347
1348    #[test]
1349    fn test_go_timeleft() {
1350        let ml = parse_strict("go wtime 903000 btime 770908 winc 15000 movestogo 17 binc 10000\n")
1351            .unwrap();
1352        assert_eq!(ml.len(), 1);
1353
1354        let tl = UciTimeControl::TimeLeft {
1355            white_time: Some(Duration::milliseconds(903000)),
1356            black_time: Some(Duration::milliseconds(770908)),
1357            white_increment: Some(Duration::milliseconds(15000)),
1358            black_increment: Some(Duration::milliseconds(10000)),
1359            moves_to_go: Some(17),
1360        };
1361
1362        assert_eq!(
1363            ml[0],
1364            UciMessage::Go {
1365                search_control: None,
1366                time_control: Some(tl),
1367            }
1368        );
1369    }
1370
1371    #[test]
1372    fn test_search_control_depth() {
1373        let ml = parse_strict("go ponder depth 6\n").unwrap();
1374        assert_eq!(ml.len(), 1);
1375
1376        let result = UciMessage::Go {
1377            time_control: Some(UciTimeControl::Ponder),
1378            search_control: Some(UciSearchControl::depth(6)),
1379        };
1380
1381        assert_eq!(ml[0], result);
1382    }
1383
1384    #[test]
1385    fn test_search_control_mate() {
1386        let ml = parse_strict("go mate 12\n").unwrap();
1387        assert_eq!(ml.len(), 1);
1388
1389        let result = UciMessage::Go {
1390            time_control: None,
1391            search_control: Some(UciSearchControl::mate(12)),
1392        };
1393
1394        assert_eq!(ml[0], result);
1395    }
1396
1397    #[test]
1398    fn test_nodes_searchmoves() {
1399        let ml = parse_strict("go nodes 79093455456 searchmoves e2e4 d2d4 g2g1n\n").unwrap();
1400        assert_eq!(ml.len(), 1);
1401
1402        #[cfg(not(feature = "chess"))]
1403        let sc = UciSearchControl {
1404            depth: None,
1405            nodes: Some(79093455456),
1406            mate: None,
1407            search_moves: vec![
1408                UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 4)),
1409                UciMove::from_to(UciSquare::from('d', 2), UciSquare::from('d', 4)),
1410                UciMove {
1411                    from: UciSquare::from('g', 2),
1412                    to: UciSquare::from('g', 1),
1413                    promotion: Some(UciPiece::Knight),
1414                },
1415            ],
1416        };
1417
1418        #[cfg(feature = "chess")]
1419        let sc = UciSearchControl {
1420            depth: None,
1421            nodes: Some(79093455456),
1422            mate: None,
1423            search_moves: vec![
1424                ChessMove::new(Square::E2, Square::E4, None),
1425                ChessMove::new(Square::D2, Square::D4, None),
1426                ChessMove::new(Square::G2, Square::G1, Some(Piece::Knight)),
1427            ],
1428        };
1429
1430        let result = UciMessage::Go {
1431            time_control: None,
1432            search_control: Some(sc),
1433        };
1434
1435        assert_eq!(ml[0], result);
1436    }
1437
1438    #[test]
1439    fn test_go_full_example() {
1440        let ml =
1441            parse_strict("go movetime 10000 searchmoves a1h8 depth 6 nodes 55000000\n").unwrap();
1442        assert_eq!(ml.len(), 1);
1443
1444        let tc = UciTimeControl::MoveTime(Duration::milliseconds(10000));
1445
1446        #[cfg(not(feature = "chess"))]
1447        let sc = UciSearchControl {
1448            depth: Some(6),
1449            nodes: Some(55000000),
1450            mate: None,
1451            search_moves: vec![UciMove::from_to(
1452                UciSquare::from('a', 1),
1453                UciSquare::from('h', 8),
1454            )],
1455        };
1456
1457        #[cfg(feature = "chess")]
1458        let sc = UciSearchControl {
1459            depth: Some(6),
1460            nodes: Some(55000000),
1461            mate: None,
1462            search_moves: vec![ChessMove::new(Square::A1, Square::H8, None)],
1463        };
1464
1465        let result = UciMessage::Go {
1466            time_control: Some(tc),
1467            search_control: Some(sc),
1468        };
1469
1470        assert_eq!(ml[0], result);
1471    }
1472
1473    #[test]
1474    fn test_two_command_doc_example() {
1475        let ml = parse_strict("position startpos\ngo ponder searchmoves e2e4 d2d4\n").unwrap();
1476        assert_eq!(ml.len(), 2);
1477    }
1478
1479    #[test]
1480    fn test_lax_mode() {
1481        let ml = parse("position startpos\nunknown command\ngo ponder searchmoves e2e4 d2d4\n");
1482        assert_eq!(ml.len(), 2);
1483
1484        match ml[0] {
1485            UciMessage::Position { .. } => {}
1486            _ => panic!("Expected a `position` message here"),
1487        };
1488
1489        match ml[1] {
1490            UciMessage::Go { .. } => {}
1491            _ => panic!("Expected a `go` message here"),
1492        };
1493    }
1494
1495    #[test]
1496    #[should_panic]
1497    fn test_strict_mode() {
1498        parse_strict("position startpos\nunknown command\ngo ponder searchmoves e2e4 d2d4\n")
1499            .unwrap();
1500    }
1501
1502    #[test]
1503    fn test_id() {
1504        let ml = parse_strict("id name Vampirc 1.0\nid    author    Matija Kejžar\n").unwrap();
1505        assert_eq!(ml.len(), 2);
1506
1507        let result = UciMessage::Id {
1508            name: Some("Vampirc 1.0".to_string()),
1509            author: None,
1510        };
1511
1512        assert_eq!(ml[0], result);
1513
1514        let result2 = UciMessage::Id {
1515            author: Some("Matija Kejžar".to_string()),
1516            name: None,
1517        };
1518
1519        assert_eq!(ml[1], result2);
1520    }
1521
1522    #[test]
1523    fn test_uciok() {
1524        let ml = parse_strict("uci\n    uciok    \r\n").unwrap();
1525        assert_eq!(ml.len(), 2);
1526        assert_eq!(ml[0], UciMessage::Uci);
1527        assert_eq!(ml[1], UciMessage::UciOk);
1528    }
1529
1530    #[test]
1531    fn test_readyok() {
1532        let ml = parse_strict("isready\nreadyok\n").unwrap();
1533        assert_eq!(ml.len(), 2);
1534        assert_eq!(ml[0], UciMessage::IsReady);
1535        assert_eq!(ml[1], UciMessage::ReadyOk);
1536    }
1537
1538    // bestmove g1f3
1539    #[test]
1540    fn test_bestmove() {
1541        let ml = parse_strict("bestmove  g1f3\n").unwrap();
1542        assert_eq!(ml.len(), 1);
1543
1544        #[cfg(not(feature = "chess"))]
1545        let m = UciMessage::BestMove {
1546            best_move: UciMove {
1547                from: UciSquare::from('g', 1),
1548                to: UciSquare::from('f', 3),
1549                promotion: None,
1550            },
1551
1552            ponder: None,
1553        };
1554
1555        #[cfg(feature = "chess")]
1556        let m = UciMessage::BestMove {
1557            best_move: ChessMove::new(Square::G1, Square::F3, None),
1558
1559            ponder: None,
1560        };
1561
1562        assert_eq!(m, ml[0]);
1563    }
1564
1565    // bestmove g1f3 ponder d8f6
1566    #[test]
1567    fn test_bestmove_with_ponder() {
1568        let ml = parse_strict("bestmove g1f3 ponder d8f6\n").unwrap();
1569        assert_eq!(ml.len(), 1);
1570
1571        #[cfg(not(feature = "chess"))]
1572        let m = UciMessage::BestMove {
1573            best_move: UciMove {
1574                from: UciSquare::from('g', 1),
1575                to: UciSquare::from('f', 3),
1576                promotion: None,
1577            },
1578
1579            ponder: Some(UciMove {
1580                from: UciSquare::from('d', 8),
1581                to: UciSquare::from('f', 6),
1582                promotion: None,
1583            }),
1584        };
1585
1586        #[cfg(feature = "chess")]
1587        let m = UciMessage::BestMove {
1588            best_move: ChessMove::new(Square::G1, Square::F3, None),
1589
1590            ponder: Some(ChessMove::new(Square::D8, Square::F6, None)),
1591        };
1592
1593        assert_eq!(m, ml[0]);
1594    }
1595
1596    #[test]
1597    fn test_copyprotection() {
1598        let ml = parse_strict("copyprotection checking\ncopyprotection   ok\n").unwrap();
1599        assert_eq!(ml.len(), 2);
1600        assert_eq!(ml[0], UciMessage::CopyProtection(ProtectionState::Checking));
1601        assert_eq!(ml[1], UciMessage::CopyProtection(ProtectionState::Ok));
1602    }
1603
1604    #[test]
1605    fn test_registration() {
1606        let ml = parse_strict("registration   checking\nregistration error\n").unwrap();
1607        assert_eq!(ml.len(), 2);
1608        assert_eq!(ml[0], UciMessage::Registration(ProtectionState::Checking));
1609        assert_eq!(ml[1], UciMessage::Registration(ProtectionState::Error));
1610    }
1611
1612    #[test]
1613    fn test_parse_option_check() {
1614        let ml = parse_strict("option name Nullmove type check default true\n").unwrap();
1615
1616        let m = UciMessage::Option(UciOptionConfig::Check {
1617            name: "Nullmove".to_string(),
1618            default: Some(true),
1619        });
1620
1621        assert_eq!(m, ml[0]);
1622    }
1623
1624    #[test]
1625    fn test_parse_option_check_no_default() {
1626        let ml = parse_strict("option    name   A long option name type  check   \n").unwrap();
1627
1628        let m = UciMessage::Option(UciOptionConfig::Check {
1629            name: "A long option name".to_string(),
1630            default: None,
1631        });
1632
1633        assert_eq!(m, ml[0]);
1634    }
1635
1636    #[test]
1637    fn test_parse_option_spin() {
1638        let ml =
1639            parse_strict("option name Selectivity type spin default 2 min 0 max 4\n\n").unwrap();
1640
1641        let m = UciMessage::Option(UciOptionConfig::Spin {
1642            name: "Selectivity".to_string(),
1643            default: Some(2),
1644            min: Some(0),
1645            max: Some(4),
1646        });
1647
1648        assert_eq!(m, ml[0]);
1649    }
1650
1651    #[test]
1652    fn test_parse_option_spin_no_default() {
1653        let ml = parse_strict(
1654            "option name A spin option without a default type spin min -5676 max -33\n",
1655        )
1656        .unwrap();
1657
1658        let m = UciMessage::Option(UciOptionConfig::Spin {
1659            name: "A spin option without a default".to_string(),
1660            default: None,
1661            min: Some(-5676),
1662            max: Some(-33),
1663        });
1664
1665        assert_eq!(m, ml[0]);
1666    }
1667
1668    #[test]
1669    fn test_parse_option_spin_just_min() {
1670        let ml = parse_strict("option name JUST MIN type spin min -40964656\n").unwrap();
1671
1672        let m = UciMessage::Option(UciOptionConfig::Spin {
1673            name: "JUST MIN".to_string(),
1674            default: None,
1675            min: Some(-40964656),
1676            max: None,
1677        });
1678
1679        assert_eq!(m, ml[0]);
1680    }
1681
1682    #[test]
1683    fn test_parse_option_spin_just_max() {
1684        let ml = parse_strict("option name just_max type spin max 56565464509\n").unwrap();
1685
1686        let m = UciMessage::Option(UciOptionConfig::Spin {
1687            name: "just_max".to_string(),
1688            default: None,
1689            max: Some(56565464509),
1690            min: None,
1691        });
1692
1693        assert_eq!(m, ml[0]);
1694    }
1695
1696    #[test]
1697    fn test_parse_option_spin_just_default_and_max() {
1698        let ml = parse_strict("option name def max type spin default -5 max 55\n").unwrap();
1699
1700        let m = UciMessage::Option(UciOptionConfig::Spin {
1701            name: "def max".to_string(),
1702            default: Some(-5),
1703            max: Some(55),
1704            min: None,
1705        });
1706
1707        assert_eq!(m, ml[0]);
1708    }
1709
1710    #[test]
1711    fn test_parse_option_combo() {
1712        let ml = parse_strict(
1713            "option name Style type combo default Normal var Solid var Normal var Risky\n",
1714        )
1715        .unwrap();
1716
1717        let m = UciMessage::Option(UciOptionConfig::Combo {
1718            name: "Style".to_string(),
1719            default: Some("Normal".to_string()),
1720            var: vec![
1721                String::from("Solid"),
1722                String::from("Normal"),
1723                String::from("Risky"),
1724            ],
1725        });
1726
1727        assert_eq!(m, ml[0]);
1728    }
1729
1730    #[test]
1731    fn test_parse_option_combo_no_default() {
1732        let ml = parse_strict(
1733            "option name Some ccccc-combo type combo      var A B C var D E   F var 1 2 3\n",
1734        )
1735        .unwrap();
1736
1737        let m = UciMessage::Option(UciOptionConfig::Combo {
1738            name: "Some ccccc-combo".to_string(),
1739            default: None,
1740            var: vec![
1741                String::from("A B C"),
1742                String::from("D E   F"),
1743                String::from("1 2 3"),
1744            ],
1745        });
1746
1747        assert_eq!(m, ml[0]);
1748    }
1749
1750    #[test]
1751    fn test_parse_option_string() {
1752        let ml = parse_strict("option name Nalimov Path  type string default c:\\\n").unwrap();
1753
1754        let m = UciMessage::Option(UciOptionConfig::String {
1755            name: "Nalimov Path".to_string(),
1756            default: Some("c:\\".to_string()),
1757        });
1758
1759        assert_eq!(m, ml[0]);
1760    }
1761
1762    #[test]
1763    fn test_parse_option_string_no_default() {
1764        let ml = parse_strict("option name NP type string\r\n").unwrap();
1765
1766        let m = UciMessage::Option(UciOptionConfig::String {
1767            name: "NP".to_string(),
1768            default: None,
1769        });
1770
1771        assert_eq!(m, ml[0]);
1772    }
1773
1774    #[test]
1775    fn test_parse_option_button() {
1776        let ml = parse_strict("option name Clear Hash type button\n").unwrap();
1777
1778        let m = UciMessage::Option(UciOptionConfig::Button {
1779            name: "Clear Hash".to_string(),
1780        });
1781
1782        assert_eq!(m, ml[0]);
1783    }
1784
1785    #[test]
1786    fn test_parse_option_button_ignore_default() {
1787        let ml =
1788            parse_strict("option name CH type button default Ignore min 5 max 6 var A var B\n")
1789                .unwrap();
1790
1791        let m = UciMessage::Option(UciOptionConfig::Button {
1792            name: "CH".to_string(),
1793        });
1794
1795        assert_eq!(m, ml[0]);
1796    }
1797
1798    #[test]
1799    fn test_parse_option_string_empty() {
1800        let ml = parse_strict("option name Nalimov Path  type string default <empty>\n").unwrap();
1801
1802        let m = UciMessage::Option(UciOptionConfig::String {
1803            name: "Nalimov Path".to_string(),
1804            default: Some("".to_string()),
1805        });
1806
1807        assert_eq!(m, ml[0]);
1808    }
1809
1810    #[test]
1811    fn test_parse_info_depth() {
1812        let ml = parse_strict("info depth 23\n").unwrap();
1813
1814        let m = UciMessage::Info(vec![UciInfoAttribute::Depth(23)]);
1815
1816        assert_eq!(m, ml[0]);
1817    }
1818
1819    #[test]
1820    fn test_parse_info_seldepth() {
1821        let ml = parse_strict("info seldepth 9\n").unwrap();
1822
1823        let m = UciMessage::Info(vec![UciInfoAttribute::SelDepth(9)]);
1824
1825        assert_eq!(m, ml[0]);
1826    }
1827
1828    #[test]
1829    fn test_parse_info_time() {
1830        let ml = parse_strict("info    time    9002\n").unwrap();
1831
1832        let m = UciMessage::Info(vec![UciInfoAttribute::Time(Duration::milliseconds(9002))]);
1833
1834        assert_eq!(m, ml[0]);
1835    }
1836
1837    #[test]
1838    fn test_parse_info_nodes() {
1839        let ml = parse_strict("info nodes    56435234425\n").unwrap();
1840
1841        let m = UciMessage::Info(vec![UciInfoAttribute::Nodes(56435234425)]);
1842
1843        assert_eq!(m, ml[0]);
1844    }
1845
1846    #[test]
1847    fn test_parse_info_currmovenum() {
1848        let ml = parse_strict("info currmovenum 102\n").unwrap();
1849
1850        let m = UciMessage::Info(vec![UciInfoAttribute::CurrMoveNum(102)]);
1851
1852        assert_eq!(m, ml[0]);
1853    }
1854
1855    #[test]
1856    fn test_parse_info_hashfull() {
1857        let ml = parse_strict("info hashfull 673\n").unwrap();
1858
1859        let m = UciMessage::Info(vec![UciInfoAttribute::HashFull(673)]);
1860
1861        assert_eq!(m, ml[0]);
1862    }
1863
1864    #[test]
1865    fn test_parse_info_nps() {
1866        let ml = parse_strict("info nps 12003\n").unwrap();
1867
1868        let m = UciMessage::Info(vec![UciInfoAttribute::Nps(12003)]);
1869
1870        assert_eq!(m, ml[0]);
1871    }
1872
1873    #[test]
1874    fn test_parse_info_tbhits() {
1875        let ml = parse_strict("info tbhits 5305\n").unwrap();
1876
1877        let m = UciMessage::Info(vec![UciInfoAttribute::TbHits(5305)]);
1878
1879        assert_eq!(m, ml[0]);
1880    }
1881
1882    #[test]
1883    fn test_parse_info_sbhits() {
1884        let ml = parse_strict("info sbhits 0\n").unwrap();
1885
1886        let m = UciMessage::Info(vec![UciInfoAttribute::SbHits(0)]);
1887
1888        assert_eq!(m, ml[0]);
1889    }
1890
1891    #[test]
1892    fn test_parse_info_cpuload() {
1893        let ml = parse_strict("info cpuload 773\n").unwrap();
1894
1895        let m = UciMessage::Info(vec![UciInfoAttribute::CpuLoad(773)]);
1896
1897        assert_eq!(m, ml[0]);
1898    }
1899
1900    #[test]
1901    fn test_parse_info_multipv() {
1902        let ml = parse_strict("info multipv 2\n").unwrap();
1903
1904        let m = UciMessage::Info(vec![UciInfoAttribute::MultiPv(2)]);
1905
1906        assert_eq!(m, ml[0]);
1907    }
1908
1909    #[test]
1910    fn test_parse_info_string() {
1911        let ml = parse_strict("info string    I am   the Walrus! Cuckoo cachoo.\n").unwrap();
1912
1913        let m = UciMessage::Info(vec![UciInfoAttribute::String(
1914            "I am   the Walrus! Cuckoo cachoo.".to_owned(),
1915        )]);
1916
1917        assert_eq!(m, ml[0]);
1918    }
1919
1920    #[test]
1921    fn test_parse_info_any() {
1922        let ml = parse_strict("info UCI_Whatever -29 A3 57\n").unwrap();
1923
1924        let m = UciMessage::Info(vec![UciInfoAttribute::Any(
1925            "UCI_Whatever".to_owned(),
1926            "-29 A3 57".to_owned(),
1927        )]);
1928
1929        assert_eq!(m, ml[0]);
1930    }
1931
1932    #[test]
1933    fn test_parse_info_currmove() {
1934        let ml = parse_strict("info currmove a7a8q\n").unwrap();
1935
1936        #[cfg(not(feature = "chess"))]
1937        let m = UciMessage::Info(vec![UciInfoAttribute::CurrMove(UciMove {
1938            from: UciSquare::from('a', 7),
1939            to: UciSquare::from('a', 8),
1940            promotion: Some(UciPiece::Queen),
1941        })]);
1942
1943        #[cfg(feature = "chess")]
1944        let m = UciMessage::Info(vec![UciInfoAttribute::CurrMove(ChessMove::new(
1945            Square::A7,
1946            Square::A8,
1947            Some(Piece::Queen),
1948        ))]);
1949
1950        assert_eq!(m, ml[0]);
1951    }
1952
1953    #[test]
1954    fn test_parse_info_pv() {
1955        let ml = parse_strict("info pv e2e4 e7e5 g1f3\n").unwrap();
1956
1957        #[cfg(not(feature = "chess"))]
1958        let m = UciMessage::Info(vec![UciInfoAttribute::Pv(vec![
1959            UciMove::from_to(UciSquare::from('e', 2), UciSquare::from('e', 4)),
1960            UciMove::from_to(UciSquare::from('e', 7), UciSquare::from('e', 5)),
1961            UciMove::from_to(UciSquare::from('g', 1), UciSquare::from('f', 3)),
1962        ])]);
1963
1964        #[cfg(feature = "chess")]
1965        let m = UciMessage::Info(vec![UciInfoAttribute::Pv(vec![
1966            ChessMove::new(Square::E2, Square::E4, None),
1967            ChessMove::new(Square::E7, Square::E5, None),
1968            ChessMove::new(Square::G1, Square::F3, None),
1969        ])]);
1970
1971        assert_eq!(m, ml[0]);
1972    }
1973
1974    #[test]
1975    fn test_parse_info_refutation() {
1976        let ml = parse_strict("info refutation d1h5 g6h5\n").unwrap();
1977
1978        #[cfg(not(feature = "chess"))]
1979        let m = UciMessage::Info(vec![UciInfoAttribute::Refutation(vec![
1980            UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
1981            UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
1982        ])]);
1983
1984        #[cfg(feature = "chess")]
1985        let m = UciMessage::Info(vec![UciInfoAttribute::Refutation(vec![
1986            ChessMove::new(Square::D1, Square::H5, None),
1987            ChessMove::new(Square::G6, Square::H5, None),
1988        ])]);
1989
1990        assert_eq!(m, ml[0]);
1991    }
1992
1993    #[test]
1994    fn test_info_currline_no_cpu_nr() {
1995        let ml = parse_strict("info currline d1h5 g6h5\n").unwrap();
1996
1997        #[cfg(not(feature = "chess"))]
1998        let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
1999            cpu_nr: None,
2000            line: vec![
2001                UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2002                UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2003            ],
2004        }]);
2005
2006        #[cfg(feature = "chess")]
2007        let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2008            cpu_nr: None,
2009            line: vec![
2010                ChessMove::new(Square::D1, Square::H5, None),
2011                ChessMove::new(Square::G6, Square::H5, None),
2012            ],
2013        }]);
2014
2015        assert_eq!(m, ml[0]);
2016    }
2017
2018    #[test]
2019    fn test_info_currline_with_cpu_nr() {
2020        let ml = parse_strict("info currline 1 d1h5 g6h5\n").unwrap();
2021
2022        #[cfg(not(feature = "chess"))]
2023        let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2024            cpu_nr: Some(1),
2025            line: vec![
2026                UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2027                UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2028            ],
2029        }]);
2030
2031        #[cfg(feature = "chess")]
2032        let m = UciMessage::Info(vec![UciInfoAttribute::CurrLine {
2033            cpu_nr: Some(1),
2034            line: vec![
2035                ChessMove::new(Square::D1, Square::H5, None),
2036                ChessMove::new(Square::G6, Square::H5, None),
2037            ],
2038        }]);
2039
2040        assert_eq!(m, ml[0]);
2041    }
2042
2043    #[test]
2044    fn test_info_currline_multi_cpu_nr() {
2045        let ml = parse_strict("info currline 1 d1h5 g6h5 currline 2 e2e4 currline 3 d2d4 d7d5\n")
2046            .unwrap();
2047
2048        #[cfg(not(feature = "chess"))]
2049        let m = UciMessage::Info(vec![
2050            UciInfoAttribute::CurrLine {
2051                cpu_nr: Some(1),
2052                line: vec![
2053                    UciMove::from_to(UciSquare::from('d', 1), UciSquare::from('h', 5)),
2054                    UciMove::from_to(UciSquare::from('g', 6), UciSquare::from('h', 5)),
2055                ],
2056            },
2057            UciInfoAttribute::CurrLine {
2058                cpu_nr: Some(2),
2059                line: vec![UciMove::from_to(
2060                    UciSquare::from('e', 2),
2061                    UciSquare::from('e', 4),
2062                )],
2063            },
2064            UciInfoAttribute::CurrLine {
2065                cpu_nr: Some(3),
2066                line: vec![
2067                    UciMove::from_to(UciSquare::from('d', 2), UciSquare::from('d', 4)),
2068                    UciMove::from_to(UciSquare::from('d', 7), UciSquare::from('d', 5)),
2069                ],
2070            },
2071        ]);
2072
2073        #[cfg(feature = "chess")]
2074        let m = UciMessage::Info(vec![
2075            UciInfoAttribute::CurrLine {
2076                cpu_nr: Some(1),
2077                line: vec![
2078                    ChessMove::new(Square::D1, Square::H5, None),
2079                    ChessMove::new(Square::G6, Square::H5, None),
2080                ],
2081            },
2082            UciInfoAttribute::CurrLine {
2083                cpu_nr: Some(2),
2084                line: vec![ChessMove::new(Square::E2, Square::E4, None)],
2085            },
2086            UciInfoAttribute::CurrLine {
2087                cpu_nr: Some(3),
2088                line: vec![
2089                    ChessMove::new(Square::D2, Square::D4, None),
2090                    ChessMove::new(Square::D7, Square::D5, None),
2091                ],
2092            },
2093        ]);
2094
2095        assert_eq!(m, ml[0]);
2096    }
2097
2098    #[test]
2099    fn test_info_score_cp() {
2100        let ml = parse_strict("info score cp 20\n").unwrap();
2101
2102        let m = UciMessage::Info(vec![UciInfoAttribute::from_centipawns(20)]);
2103
2104        assert_eq!(m, ml[0]);
2105    }
2106
2107    #[test]
2108    fn test_info_score_mate() {
2109        let ml = parse_strict("info score mate -3\n").unwrap();
2110
2111        let m = UciMessage::Info(vec![UciInfoAttribute::from_mate(-3)]);
2112
2113        assert_eq!(m, ml[0]);
2114    }
2115
2116    #[test]
2117    fn test_info_score_cp_lowerbound() {
2118        let ml = parse_strict("info score cp -75 lowerbound\n").unwrap();
2119
2120        let m = UciMessage::Info(vec![UciInfoAttribute::Score {
2121            cp: Some(-75),
2122            mate: None,
2123            lower_bound: Some(true),
2124            upper_bound: None,
2125        }]);
2126
2127        assert_eq!(m, ml[0]);
2128    }
2129
2130    #[test]
2131    fn test_info_score_cp_upperbound() {
2132        let ml = parse_strict("info score cp 404 upperbound\n").unwrap();
2133
2134        let m = UciMessage::Info(vec![UciInfoAttribute::Score {
2135            cp: Some(404),
2136            mate: None,
2137            upper_bound: Some(true),
2138            lower_bound: None,
2139        }]);
2140
2141        assert_eq!(m, ml[0]);
2142    }
2143
2144    // info score cp 13  depth 1 nodes 13 time 15 pv f1b5
2145    #[test]
2146    fn test_info_multi1() {
2147        let ml = parse_strict("info score cp 13  depth 1 nodes 13 time 15 pv f1b5\n").unwrap();
2148        println!("{}", ml[0].serialize());
2149        assert_eq!(1, ml.len());
2150
2151        #[cfg(not(feature = "chess"))]
2152        let m = UciMessage::Info(vec![
2153            UciInfoAttribute::from_centipawns(13),
2154            UciInfoAttribute::Depth(1),
2155            UciInfoAttribute::Nodes(13),
2156            UciInfoAttribute::Time(Duration::milliseconds(15)),
2157            UciInfoAttribute::Pv(vec![UciMove::from_to(
2158                UciSquare::from('f', 1),
2159                UciSquare::from('b', 5),
2160            )]),
2161        ]);
2162
2163        #[cfg(feature = "chess")]
2164        let m = UciMessage::Info(vec![
2165            UciInfoAttribute::from_centipawns(13),
2166            UciInfoAttribute::Depth(1),
2167            UciInfoAttribute::Nodes(13),
2168            UciInfoAttribute::Time(Duration::milliseconds(15)),
2169            UciInfoAttribute::Pv(vec![ChessMove::new(Square::F1, Square::B5, None)]),
2170        ]);
2171
2172        assert_eq!(m, ml[0]);
2173
2174        assert_eq!(
2175            m.serialize(),
2176            "info score cp 13 depth 1 nodes 13 time 15 pv f1b5"
2177        )
2178    }
2179
2180    // info depth 2 seldepth 2
2181    #[test]
2182    fn test_info_multi2() {
2183        let ml = parse_strict("info depth 2 seldepth 2\n").unwrap();
2184        println!("{}", ml[0].serialize());
2185        assert_eq!(1, ml.len());
2186
2187        let m = UciMessage::Info(vec![
2188            UciInfoAttribute::Depth(2),
2189            UciInfoAttribute::SelDepth(2),
2190        ]);
2191
2192        assert_eq!(m, ml[0]);
2193        assert_eq!(m.serialize(), "info depth 2 seldepth 2")
2194    }
2195
2196    // info score cp 20  depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3
2197    #[test]
2198    fn test_info_multi3() {
2199        let ml = parse_strict("info score cp 20  depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3 \n")
2200            .unwrap();
2201        println!("{}", ml[0].serialize());
2202        assert_eq!(1, ml.len());
2203
2204        #[cfg(not(feature = "chess"))]
2205        let m = UciMessage::Info(vec![
2206            UciInfoAttribute::from_centipawns(20),
2207            UciInfoAttribute::Depth(3),
2208            UciInfoAttribute::Nodes(423),
2209            UciInfoAttribute::Time(Duration::milliseconds(15)),
2210            UciInfoAttribute::Pv(vec![
2211                UciMove::from_to(UciSquare::from('f', 1), UciSquare::from('c', 4)),
2212                UciMove::from_to(UciSquare::from('g', 8), UciSquare::from('f', 6)),
2213                UciMove::from_to(UciSquare::from('b', 1), UciSquare::from('c', 3)),
2214            ]),
2215        ]);
2216
2217        #[cfg(feature = "chess")]
2218        let m = UciMessage::Info(vec![
2219            UciInfoAttribute::from_centipawns(20),
2220            UciInfoAttribute::Depth(3),
2221            UciInfoAttribute::Nodes(423),
2222            UciInfoAttribute::Time(Duration::milliseconds(15)),
2223            UciInfoAttribute::Pv(vec![
2224                ChessMove::new(Square::F1, Square::C4, None),
2225                ChessMove::new(Square::G8, Square::F6, None),
2226                ChessMove::new(Square::B1, Square::C3, None),
2227            ]),
2228        ]);
2229
2230        assert_eq!(m, ml[0]);
2231
2232        assert_eq!(
2233            m.serialize(),
2234            "info score cp 20 depth 3 nodes 423 time 15 pv f1c4 g8f6 b1c3"
2235        )
2236    }
2237
2238    #[test]
2239    fn test_parse_with_unknown() {
2240        let ml = parse_with_unknown("not really a message\n");
2241        assert_eq!(1, ml.len());
2242        assert_eq!(ml[0].is_unknown(), true);
2243
2244        match &ml[0] {
2245            UciMessage::Unknown(msg, _) => {
2246                assert_eq!(msg.as_str(), "not really a message");
2247            }
2248            _ => panic!("Expected a message of type UnknownMessage"),
2249        }
2250    }
2251
2252    #[test]
2253    fn test_parse_with_unknown_success() {
2254        let ml = parse_with_unknown("uci\nuciok\n");
2255        assert_eq!(2, ml.len());
2256        assert_eq!(ml[0].is_unknown(), false);
2257        assert_eq!(ml[1].is_unknown(), false);
2258
2259        assert_eq!(ml, vec![UciMessage::Uci, UciMessage::UciOk]);
2260    }
2261
2262    #[test]
2263    fn test_parse_go() {
2264        parse("go\n");
2265        let msgs = parse_strict("go\n").unwrap();
2266        assert_eq!(msgs.len(), 1);
2267        assert_eq!(msgs[0], UciMessage::go())
2268    }
2269
2270    #[test]
2271    fn test_parse_go_with_space() {
2272        parse("go\n");
2273        let msgs = parse_strict("go     \n").unwrap();
2274        assert_eq!(msgs.len(), 1);
2275        assert_eq!(msgs[0], UciMessage::go())
2276    }
2277
2278    #[test]
2279    fn test_parse_go_eoi() {
2280        parse("go");
2281        let msg = parse_one("go");
2282        assert_eq!(msg, UciMessage::go())
2283    }
2284
2285    #[ignore]
2286    #[test]
2287    fn test_parse_stdin() {
2288        println!("Enter uci command: ");
2289        for line in stdin().lock().lines() {
2290            println!("got line: {:?}", line);
2291            let msgs: MessageList = parse(&line.unwrap());
2292            for msg in msgs {
2293                println!("parsed: {}", msg);
2294            }
2295        }
2296    }
2297
2298    #[test]
2299    fn test_no_line_at_end_multi_parse() {
2300        let msgs = parse("uci\ndebug on\nucinewgame\nstop\nquit");
2301        assert_eq!(msgs.len(), 5);
2302        assert_eq!(msgs[0], UciMessage::Uci);
2303        assert_eq!(msgs[1], UciMessage::Debug(true));
2304        assert_eq!(msgs[2], UciMessage::UciNewGame);
2305        assert_eq!(msgs[3], UciMessage::Stop);
2306        assert_eq!(msgs[4], UciMessage::Quit);
2307    }
2308
2309    #[test]
2310    fn test_no_line_at_end_single_parse() {
2311        let msgs = parse("uci\n");
2312        assert_eq!(msgs.len(), 1);
2313        assert_eq!(msgs[0], UciMessage::Uci);
2314
2315        let msgs2 = parse("go ponder\n");
2316        assert_eq!(msgs2.len(), 1);
2317        assert_eq!(msgs2[0], UciMessage::go_ponder());
2318    }
2319
2320    #[test]
2321    fn test_no_line_at_end_multi_parse_strict() {
2322        let msgs = parse("uci\ndebug on\nucinewgame\nstop\nquit");
2323        assert_eq!(msgs.len(), 5);
2324        assert_eq!(msgs[0], UciMessage::Uci);
2325        assert_eq!(msgs[1], UciMessage::Debug(true));
2326        assert_eq!(msgs[2], UciMessage::UciNewGame);
2327        assert_eq!(msgs[3], UciMessage::Stop);
2328        assert_eq!(msgs[4], UciMessage::Quit);
2329    }
2330
2331    #[test]
2332    fn test_no_line_at_end_single_parse_strict() {
2333        let msgs = parse_strict("uci\n").unwrap();
2334        assert_eq!(msgs.len(), 1);
2335        assert_eq!(msgs[0], UciMessage::Uci);
2336
2337        let msgs2 = parse_strict("info score cp 20").unwrap();
2338        assert_eq!(msgs2.len(), 1);
2339        assert_eq!(
2340            msgs2[0],
2341            UciMessage::Info(vec![UciInfoAttribute::Score {
2342                cp: Some(20),
2343                lower_bound: None,
2344                mate: None,
2345                upper_bound: None,
2346            }])
2347        );
2348    }
2349
2350    #[test]
2351    fn test_no_line_at_end_parse_with_unknown_with_unknown() {
2352        let msgs = parse_with_unknown("uci\ndebug on\nucinewgame\nabc\nstop\nquit");
2353        assert_eq!(msgs.len(), 6);
2354        assert_eq!(msgs[0], UciMessage::Uci);
2355        assert_eq!(msgs[1], UciMessage::Debug(true));
2356        assert_eq!(msgs[2], UciMessage::UciNewGame);
2357        assert_eq!(msgs[3], UciMessage::Unknown("abc".to_string(), None));
2358        assert_eq!(msgs[4], UciMessage::Stop);
2359        assert_eq!(msgs[5], UciMessage::Quit);
2360    }
2361
2362    #[test]
2363    fn test_complex_parse_with_unknown() {
2364        let msgs = parse_with_unknown("I am the walrus\nuci\ndebug on\nShould I stay \
2365        or should I go?\nLondon calling\nquit\nAre we there yet?\n");
2366        assert_eq!(msgs.len(), 7);
2367        assert_eq!(msgs[0], UciMessage::Unknown("I am the walrus".to_string(), None));
2368        assert_eq!(msgs[1], UciMessage::Uci);
2369        assert_eq!(msgs[2], UciMessage::Debug(true));
2370        assert_eq!(msgs[3], UciMessage::Unknown("Should I stay or should I go?".to_string(), None));
2371        assert_eq!(msgs[4], UciMessage::Unknown("London calling".to_string(), None));
2372        assert_eq!(msgs[5], UciMessage::Quit);
2373        assert_eq!(msgs[6], UciMessage::Unknown("Are we there yet?".to_string(), None));
2374    }
2375
2376    #[test]
2377    fn test_no_line_at_end_parse_with_unknown() {
2378        let msgs = parse_with_unknown("uci\ndebug on\nucinewgame\nstop\nquit");
2379        assert_eq!(msgs.len(), 5);
2380        assert_eq!(msgs[0], UciMessage::Uci);
2381        assert_eq!(msgs[1], UciMessage::Debug(true));
2382        assert_eq!(msgs[2], UciMessage::UciNewGame);
2383        assert_eq!(msgs[3], UciMessage::Stop);
2384        assert_eq!(msgs[4], UciMessage::Quit);
2385    }
2386
2387    #[test]
2388    fn test_no_line_at_end_parse_with_unknown_single() {
2389        let msgs = parse_with_unknown("uciok");
2390        assert_eq!(msgs.len(), 1);
2391        assert_eq!(msgs[0], UciMessage::UciOk);
2392    }
2393
2394    #[test]
2395    fn test_empty_nl_parse() {
2396        let msgs = parse("\n");
2397        assert_eq!(msgs.len(), 0);
2398    }
2399
2400    #[test]
2401    fn test_empty_nl_parse_strict() {
2402        let msgs = parse_strict("\n").unwrap();
2403        assert_eq!(msgs.len(), 0);
2404    }
2405
2406    #[test]
2407    fn test_empty_nl_parse_with_unknown() {
2408        let msgs = parse_with_unknown("\n");
2409        assert_eq!(msgs.len(), 0);
2410    }
2411
2412    #[test]
2413    fn test_empty_parse() {
2414        let msgs = parse("");
2415        assert_eq!(msgs.len(), 0);
2416    }
2417
2418    #[test]
2419    fn test_empty_parse_strict() {
2420        let msgs = parse_strict("").unwrap();
2421        assert_eq!(msgs.len(), 0);
2422    }
2423
2424    #[test]
2425    fn test_empty_parse_with_unknown() {
2426        let msgs = parse_with_unknown("");
2427        assert_eq!(msgs.len(), 0);
2428    }
2429
2430    #[test]
2431    fn test_parse_one_uci() {
2432        let msg = parse_one("uci");
2433        assert_eq!(msg, UciMessage::Uci);
2434    }
2435
2436    #[test]
2437    fn test_parse_one_go() {
2438        let msg = parse_one("go    infinite   \n");
2439        assert_eq!(msg, UciMessage::go_infinite());
2440    }
2441
2442    #[test]
2443    fn test_parse_one_empty() {
2444        let msg = parse_one("");
2445        match msg {
2446            UciMessage::Unknown(s, _) => {
2447                assert_eq!(s, String::new());
2448            }
2449            _ => panic!("Expected UciMessage::Unknown"),
2450        }
2451    }
2452
2453    #[test]
2454    fn test_parse_one_unknown() {
2455        let msg = parse_one("ax34\n");
2456        match msg {
2457            UciMessage::Unknown(s, _) => {
2458                assert_eq!(s, String::from("ax34"));
2459            }
2460            _ => panic!("Expected UciMessage::Unknown"),
2461        }
2462    }
2463
2464    #[test]
2465    fn test_parse_one_multi_commands() {
2466        let msg = parse_one("uci\nuciok\n");
2467        assert_eq!(msg, UciMessage::Uci);
2468    }
2469
2470    #[test]
2471    fn test_parse_negative_duration_wtime() {
2472        let parsed_msg = parse_one("go wtime -4061 btime 56826 movestogo 90\n");
2473
2474        let time_control = UciTimeControl::TimeLeft {
2475            white_time: Some(Duration::milliseconds(-4061)),
2476            black_time: Some(Duration::milliseconds(56826)),
2477            white_increment: None,
2478            black_increment: None,
2479            moves_to_go: Some(90),
2480        };
2481
2482        let test_msg = UciMessage::Go {
2483            time_control: Some(time_control),
2484            search_control: None,
2485        };
2486
2487        assert_eq!(test_msg, parsed_msg);
2488    }
2489
2490    #[test]
2491    fn test_parse_signed_positive_duration_wtime() {
2492        let parsed_msg = parse_one("go wtime +15030 btime +56826 movestogo 90\n");
2493
2494        let time_control = UciTimeControl::TimeLeft {
2495            white_time: Some(Duration::milliseconds(15030)),
2496            black_time: Some(Duration::milliseconds(56826)),
2497            white_increment: None,
2498            black_increment: None,
2499            moves_to_go: Some(90),
2500        };
2501
2502        let test_msg = UciMessage::Go {
2503            time_control: Some(time_control),
2504            search_control: None,
2505        };
2506
2507        assert_eq!(test_msg, parsed_msg);
2508    }
2509
2510    #[test]
2511    fn test_parse_signed_improperly_duration_wtime() {
2512        let parsed_msg = parse_one("go wtime !15030 btime +56826 movestogo 90\n");
2513
2514        match parsed_msg {
2515            UciMessage::Unknown(cmd, err) => {
2516                assert_eq!(cmd, "go wtime !15030 btime +56826 movestogo 90");
2517                assert!(err.is_some());
2518            }
2519            _ => unreachable!(),
2520        }
2521    }
2522
2523    #[test]
2524    fn test_parse_signed_improperly_duration_wtime_ignore() {
2525        let parsed_msg = parse("go wtime -15030 btime @56826 movestogo 90\n");
2526        assert!(parsed_msg.is_empty());
2527    }
2528
2529    #[test]
2530    fn test_parse_signed_improperly_duration_wtime_strict() {
2531        let err = parse_strict("go wtime -15030 btime x56826 movestogo 90\n");
2532        assert!(err.is_err());
2533        let e: pest::error::Error<_> = err.unwrap_err();
2534        match e.variant {
2535            pest::error::ErrorVariant::ParsingError {
2536                positives,
2537                negatives: _,
2538            } => {
2539                assert!(!positives.is_empty());
2540            }
2541            _ => unreachable!(),
2542        }
2543    }
2544}