gtp/
lib.rs

1// Copyright (c) 2019 Weird Constructor <weirdconstructor@gmail.com>
2// This is a part of gtp-rs. See README.md and COPYING for details.
3
4/*!
5A GTP (Go Text Protocol) controller implementation for Rust
6===========================================================
7
8This crate implements a parser, serializer for the Go Text Protocol and an
9abstraction for an GTP engine controller.  You may just use the protocol
10handling parts if you have an alternative GTP engine controller implementation.
11But you are free to use the one provided by this crate.
12
13See also:
14
15* [GTP (Go Text Protocol)](https://www.lysator.liu.se/~gunnar/gtp/)
16
17# Usage
18
19A short overview of the API of this crate.
20
21## Talking with a GTP engine
22
23This is the basic usage on how to communicate with a GTP
24engine like `GNU Go`, `Leela Zero` or `KataGo`:
25
26```
27use std::time::Duration;
28use gtp::Command;
29use gtp::controller::Engine;
30
31let mut ctrl = Engine::new("/usr/bin/gnugo", &["--mode", "gtp"]);
32assert!(ctrl.start().is_ok());
33
34ctrl.send(Command::cmd("name", |e| e));
35let resp = ctrl.wait_response(Duration::from_millis(500)).unwrap();
36let ev = resp.entities(|ep| ep.s().s()).unwrap();
37assert_eq!(ev[0].to_string(), "GNU");
38assert_eq!(ev[1].to_string(), "Go");
39assert_eq!(resp.text(), "GNU Go");
40```
41
42## Encoding GTP commands and Decoding GTP responses
43
44Sending commands:
45
46```
47use gtp;
48let mut c = gtp::Command::new("list_commands");
49assert_eq!(c.to_string(), "list_commands\n");
50```
51
52Sending commands with entities:
53
54```
55use gtp::Command;
56assert_eq!(Command::new("clear_board").to_string(), "clear_board\n");
57assert_eq!(Command::new_with_args("boardsize", |eb| eb.i(19)).to_string(),
58           "boardsize 19\n");
59```
60
61Receiving Responses:
62
63```
64let mut rp = gtp::ResponseParser::new();
65rp.feed("= o");
66rp.feed("k\n\n");
67rp.feed("= A\nB\nC\n\n= white b3 b T19\n\n");
68
69assert_eq!(rp.get_response().unwrap().text(), "ok");
70assert_eq!(rp.get_response().unwrap().text(), "A\nB\nC");
71
72// And processing entities in the response:
73let ents = rp.get_response().unwrap()
74             .entities(|ep| ep.color().vertex().mv()).unwrap();
75assert_eq!(ents[0].to_string(), "w");
76assert_eq!(ents[1].to_string(), "B3");
77assert_eq!(ents[2].to_string(), "b T19");
78
79// And processing entities in the response more complicatedly:
80rp.feed("= white b3\n\n");
81
82let mut ep = gtp::EntityParser::new(&rp.get_response().unwrap().text());
83let res = ep.mv().result().unwrap();
84assert_eq!(res[0].to_string(), "w B3");
85
86match res[0] {
87    gtp::Entity::Move((color, (h, v))) => {
88        assert_eq!(color, gtp::Color::W);
89        assert_eq!(h, 2);
90        assert_eq!(v, 3);
91    },
92    _ => {},
93}
94```
95
96# License
97
98This project is licensed under the GNU General Public License Version 3 or
99later.
100
101## Why GPL?
102
103Picking a license for my code bothered me for a long time. I read many
104discussions about this topic. Read the license explanations. And discussed
105this matter with other developers.
106
107First about _why I write code for free_ at all:
108
109- It's my passion to write computer programs. In my free time I can
110write the code I want, when I want and the way I want. I can freely
111allocate my time and freely choose the projects I want to work on.
112- To help a friend or member of my family.
113- To solve a problem I have.
114
115Those are the reasons why I write code for free. Now the reasons
116_why I publish the code_, when I could as well keep it to myself:
117
118- So that it may bring value to users and the free software community.
119- Show my work as an artist.
120- To get into contact with other developers.
121- And it's a nice change to put some more polish on my private projects.
122
123Most of those reasons don't yet justify GPL. The main point of the GPL, as far
124as I understand: The GPL makes sure the software stays free software until
125eternity. That the user of the software always stays in control. That the users
126have _at least the means_ to adapt the software to new platforms or use cases.
127Even if the original authors don't maintain the software anymore.
128It ultimately prevents _"vendor lock in"_. I really dislike vendor lock in,
129especially as developer. Especially as developer I want and need to stay
130in control of the computers I use.
131
132Another point is, that my work has a value. If I give away my work without
133_any_ strings attached, I effectively work for free. Work for free for
134companies. I would compromise the price I can demand for my skill, workforce
135and time.
136
137This makes two reasons for me to choose the GPL:
138
1391. I do not want to support vendor lock in scenarios. At least not for free.
140   I want to prevent those when I have a choice.
141   And before you ask, yes I work for a company that sells closed source
142   software. I am not happy about the closed source fact.
143   But it pays my bills and gives me the freedom to write free software
144   in my free time.
1452. I don't want to low ball my own wage and prices by giving away free software
146   with no strings attached (for companies).
147
148## If you need a permissive or private license (MIT)
149
150Please contact me if you need a different license and really want to use
151my code. As long as I am the only author, I can change the license.
152We might find an agreement.
153
154# Contribution
155
156Unless you explicitly state otherwise, any contribution intentionally submitted
157for inclusion in gtp-rs by you, shall be licensed as GPLv3 or later,
158without any additional terms or conditions.
159
160# Authors
161
162* Weird Constructor <weirdconstructor@gmail.com>
163  (You may find me as `WeirdConstructor` on the Rust Discord.)
164
165*/
166
167pub mod controller;
168pub mod detached_command;
169
170/// The color of a move
171#[derive(Debug, Clone, Copy, PartialEq)]
172pub enum Color {
173    W,
174    B,
175}
176
177/// Helper class for constructing an Entity data structure.
178///
179/// Use it like this:
180/// ```
181/// use gtp;
182/// let mut eb = gtp::EntityBuilder::new();
183/// eb.v((19, 19));
184/// assert_eq!(eb.build().to_string(), "T19");
185/// ```
186///
187/// Alternatively you can use the [`entity`](fn.entity.html) function:
188///
189/// ```
190/// use gtp;
191/// let ent = gtp::entity(|eb| eb.v((19, 19)));
192/// assert_eq!(ent.to_string(), "T19");
193/// ```
194#[derive(Debug, Clone, PartialEq, Default)]
195pub struct EntityBuilder {
196    list:    Vec<Entity>,
197    current: Option<Entity>,
198}
199
200/// Entity building helper function.
201///
202/// If no entity was constructed in the callback an entity
203/// with an empty string is returned.
204///
205/// ```
206/// use gtp;
207/// assert_eq!(gtp::entity(|eb| eb.v_pass()).to_string(), "pass");
208/// ```
209pub fn entity<T>(f: T) -> Entity
210    where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
211    let mut b = EntityBuilder::new();
212    f(&mut b);
213    if b.has_any() {
214        b.build()
215    } else {
216        Entity::String(String::from(""))
217    }
218}
219
220impl EntityBuilder {
221    /// Constructs a new entity builder.
222    ///
223    /// Please note there are helper functions like [`entity`](fn.entity.html)
224    /// Or [`args` of Command](struct.Command.html#method.args).
225    pub fn new() -> EntityBuilder {
226        EntityBuilder::default()
227    }
228
229    pub fn i(&mut self, i: u32) -> &mut Self {
230        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
231
232        self.current = Some(Entity::Int(i));
233        self
234    }
235
236    pub fn f(&mut self, f: f32) -> &mut Self {
237        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
238
239        self.current = Some(Entity::Float(f));
240        self
241    }
242
243    pub fn s(&mut self, s: &str) -> &mut Self {
244        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
245
246        self.current = Some(Entity::String(s.to_string()));
247        self
248    }
249
250    pub fn v_pass(&mut self) -> &mut Self {
251        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
252
253        self.current = Some(Entity::Vertex((0, 0)));
254        self
255    }
256
257    pub fn v(&mut self, v: (i32, i32)) -> &mut Self {
258        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
259
260        self.current = Some(Entity::Vertex(v));
261        self
262    }
263
264    pub fn w(&mut self) -> &mut Self {
265        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
266
267        self.current = Some(Entity::Color(Color::W));
268        self
269    }
270
271    pub fn b(&mut self) -> &mut Self {
272        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
273
274        self.current = Some(Entity::Color(Color::B));
275        self
276    }
277
278    pub fn bool(&mut self, b: bool) -> &mut Self {
279        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
280
281        self.current = Some(Entity::Boolean(b));
282        self
283    }
284
285    pub fn color(&mut self, b: bool) -> &mut Self {
286        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
287
288        self.current = Some(Entity::Color(if b { Color::W } else { Color::B }));
289        self
290    }
291
292    pub fn mv_w(&mut self, v: (i32, i32)) -> &mut Self {
293        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
294
295        self.current = Some(Entity::Move((Color::W, v)));
296        self
297    }
298
299    pub fn mv_b(&mut self, v: (i32, i32)) -> &mut Self {
300        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
301
302        self.current = Some(Entity::Move((Color::B, v)));
303        self
304    }
305
306    pub fn mv(&mut self, color: bool, v: (i32, i32)) -> &mut Self {
307        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
308
309        self.current = Some(Entity::Move((if color { Color::W } else { Color::B }, v)));
310        self
311    }
312
313    pub fn list(&mut self) -> &mut Self {
314        if self.current.is_some() { self.list.push(self.current.take().unwrap()); }
315
316        self.current = Some(Entity::List(self.list.clone()));
317        self.list = Vec::new();
318        self
319    }
320
321    pub fn has_any(&self) -> bool { self.current.is_some() }
322
323    pub fn build(&self) -> Entity {
324        self.current.clone().expect("Did not setup any entitiy in EntityBuilder!")
325    }
326}
327
328#[derive(Debug, Clone, PartialEq)]
329pub enum Entity {
330    Int(u32),
331    Float(f32),
332    String(String),
333    Vertex((i32, i32)),
334    Color(Color),
335    Move((Color, (i32, i32))),
336    Boolean(bool),
337    List(Vec<Entity>),
338}
339
340fn gen_move_char(i: u32) -> char {
341    let c = if i <= 8 {
342        ('A' as u32) + (i - 1)
343    } else {
344        ('A' as u32) + i
345    };
346    if let Some(c) = std::char::from_u32(c) {
347        c
348    } else {
349        'Z'
350    }
351}
352
353impl std::fmt::Display for Entity {
354    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
355        match self {
356            Entity::Int(i)      => write!(f, "{}", i),
357            Entity::Float(n)    => write!(f, "{}", n),
358            Entity::String(s)   => write!(f, "{}", s),
359            Entity::Vertex((h, v)) => {
360                let mut s = String::from("");
361                if *h <= 0 || *v <= 0 {
362                    s += &"pass".to_string();
363                } else {
364                    s += &format!("{}", gen_move_char(*h as u32));
365                    s += &format!("{}", v);
366                }
367                write!(f, "{}", s)
368            },
369            Entity::Color(Color::W) => write!(f, "w"),
370            Entity::Color(Color::B) => write!(f, "b"),
371            Entity::Move((Color::W, (h, v))) => {
372                let mut s = String::from("");
373                if *h <= 0 || *v <= 0 {
374                    s += &"w pass".to_string();
375                } else {
376                    s += &format!("w {}", gen_move_char(*h as u32));
377                    s += &format!("{}", v);
378                }
379                write!(f, "{}", s)
380            },
381            Entity::Move((Color::B, (h, v))) => {
382                let mut s = String::from("");
383                if *h <= 0 || *v <= 0 {
384                    s += &"b pass".to_string();
385                } else {
386                    s += &format!("b {}", gen_move_char(*h as u32));
387                    s += &format!("{}", v);
388                }
389                write!(f, "{}", s)
390            },
391            Entity::Boolean(true) => write!(f, "true"),
392            Entity::Boolean(false) => write!(f, "false"),
393            Entity::List(vec) => {
394
395                let mut s = String::from("");
396                if vec.is_empty() { return write!(f, ""); }
397
398                // Try to handle at least 2 dimensional lists
399                // on output correctly:
400                let sep = if let Entity::List(_) = vec[0] {
401                    "\n"
402                } else {
403                    " "
404                };
405
406                for (i, e) in vec.iter().enumerate() {
407                    if i > 0 { s += sep; }
408                    s += &e.to_string();
409                }
410                write!(f, "{}", s)
411            }
412        }
413    }
414}
415
416#[derive(Debug, Clone, PartialEq)]
417pub struct EntityParser {
418    buffer:         String,
419    entities:       Vec<Entity>,
420    parse_error:    bool,
421}
422
423impl std::iter::Iterator for EntityParser {
424    type Item = String;
425
426    fn next(&mut self) -> Option<String> {
427        self.buffer = self.buffer.chars().skip_while(|c| *c == ' ' || *c == '\n').collect();
428
429        let mut s = String::from("");
430        let mut skip_count = 0;
431        for c in self.buffer.chars() {
432            skip_count += 1;
433            if c == ' ' || c == '\n' { break; }
434            s.push(c);
435        }
436
437        self.buffer = self.buffer.chars().skip(skip_count).collect();
438
439        if s.is_empty() { None } else { Some(s) }
440    }
441
442}
443
444impl EntityParser {
445    pub fn new(s: &str) -> Self {
446        EntityParser {
447            buffer:     String::from(s),
448            entities:   Vec::new(),
449            parse_error: false,
450        }
451    }
452
453    pub fn result(&self) -> Option<Vec<Entity>> {
454        if self.parse_error { return None; }
455        Some(self.entities.clone())
456    }
457
458    pub fn is_eof(&self) -> bool { self.buffer.is_empty() }
459    pub fn had_parse_error(&self) -> bool { self.parse_error }
460
461    pub fn s(&mut self) -> &mut Self {
462        let s = self.next().unwrap_or_else(|| String::from(""));
463        if s.is_empty() { self.parse_error = true; return self; }
464        self.entities.push(Entity::String(s));
465        self
466    }
467
468    pub fn i(&mut self) -> &mut Self {
469        let s = self.next().unwrap_or_else(|| String::from(""));
470        if let Ok(i) = s.parse::<u32>() {
471            self.entities.push(Entity::Int(i));
472        } else {
473            self.parse_error = true;
474        }
475        self
476    }
477
478    pub fn f(&mut self) -> &mut Self {
479        let s = self.next().unwrap_or_else(|| String::from(""));
480        if let Ok(f) = s.parse::<f32>() {
481            self.entities.push(Entity::Float(f));
482        } else {
483            self.parse_error = true;
484        }
485        self
486    }
487
488    pub fn color(&mut self) -> &mut Self {
489        let s = self.next().unwrap_or_else(|| String::from(""));
490        let s = s.to_lowercase();
491        if s == "w" || s == "white" { self.entities.push(Entity::Color(Color::W)); return self; }
492        if s == "b" || s == "black" { self.entities.push(Entity::Color(Color::B)); return self; }
493        self.parse_error = true;
494        self
495    }
496
497    pub fn vertex(&mut self) -> &mut Self {
498        let s = self.next().unwrap_or_else(|| String::from(""));
499        let s = s.to_uppercase();
500        if s == "PASS" { self.entities.push(Entity::Vertex((0, 0))); return self; }
501        if s.len() < 2 || s.len() > 3 {
502            self.parse_error = true;
503            return self;
504        }
505
506        let h = s.chars().nth(0).unwrap();
507        if !h.is_ascii_alphabetic() {
508            self.parse_error = true;
509            return self;
510        }
511        let h = h as u32;
512        let mut h = (h - ('A' as u32)) + 1;
513        if h > 8 { h -= 1; }
514
515        let v : String = s.chars().skip(1).collect();
516        if let Ok(v) = i32::from_str_radix(&v, 10) {
517            self.entities.push(Entity::Vertex((h as i32, v)));
518        } else {
519            self.parse_error = true;
520        }
521
522        self
523    }
524
525    pub fn mv(&mut self) -> &mut Self {
526        self.color();
527        if self.parse_error { return self; }
528        self.vertex();
529        if self.parse_error { self.entities.pop(); }
530
531        let m = self.entities.pop().unwrap();
532        let c = self.entities.pop().unwrap();
533
534        if let Entity::Vertex((h, v)) = m {
535            if let Entity::Color(c) = c {
536                self.entities.push(Entity::Move((c, (h, v))));
537                return self;
538            }
539        }
540
541        self.parse_error = true;
542        self
543    }
544
545    pub fn bool(&mut self) -> &mut Self {
546        let s = self.next().unwrap_or_else(|| String::from(""));
547        let s = s.to_uppercase();
548        if s == "TRUE" { self.entities.push(Entity::Boolean(true)); return self; }
549        if s == "FALSE" { self.entities.push(Entity::Boolean(false)); return self; }
550        self.parse_error = true;
551        self
552    }
553}
554
555/// Representation of a GTP controller to engine command.
556#[derive(Debug, Clone, PartialEq)]
557pub struct Command {
558    id:     Option<u32>,
559    name:   String,
560    args:   Option<Entity>,
561}
562
563impl Command {
564    /// Constructs a new GTP controller command to be sent to the
565    /// GTP engine.
566    ///
567    /// ```
568    /// use gtp;
569    ///
570    /// let mut c = gtp::Command::new("list_commands");
571    /// c.args(|eb| eb.i(10).f(10.20).s("OK").list());
572    /// // send with:
573    /// let gtp_bytes = c.to_bytes();
574    /// // or if you need the string:
575    /// let gtp_str = c.to_string();
576    /// ```
577    pub fn new(name: &str) -> Command {
578        Command {
579            name: String::from(name),
580            id:   None,
581            args: None,
582        }
583    }
584
585    /// Builds a new GTP engine command ready with the arguments:
586    ///
587    /// ```
588    /// use gtp;
589    /// assert_eq!(
590    ///     gtp::Command::new_with_args("boardsize", |eb| eb.i(9)).to_string(),
591    ///     "boardsize 9\n");
592    /// ```
593    pub fn new_with_args<T>(name: &str, args: T) -> Command
594        where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
595        let mut cmd = Self::new(name);
596        cmd.args(args);
597        cmd
598    }
599
600    /// Shorthand for `Command::new_with_args`.
601    pub fn cmd<T>(name: &str, args: T) -> Command
602        where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
603        Command::new_with_args(name, args)
604    }
605
606    /// Sets the ID of the command.
607    ///
608    /// ```
609    /// use gtp;
610    ///
611    /// let mut c = gtp::Command::new("list_commands");
612    /// c.set_id(12);
613    /// assert_eq!(c.to_string(), "12 list_commands\n");
614    /// ```
615    pub fn set_id(&mut self, id: u32) {
616        self.id = Some(id);
617    }
618
619    /// Helper function to construct Entity arguments for this Command.
620    ///
621    /// ```
622    /// use gtp;
623    ///
624    /// let mut c = gtp::Command::new("list_commands");
625    /// c.args(|eb| eb.i(10).f(10.20).s("OK").list());
626    /// assert_eq!(c.to_string(), "list_commands 10 10.2 OK\n");
627    /// ```
628    pub fn args<T>(&mut self, f: T)
629        where T: Fn(&mut EntityBuilder) -> &mut EntityBuilder {
630        let mut b = EntityBuilder::new();
631        f(&mut b);
632        if b.has_any() {
633            self.set_args(&b.build());
634        }
635    }
636
637
638    /// Function to set Entity arguments:
639    ///
640    /// ```
641    /// use gtp;
642    ///
643    /// let mut c = gtp::Command::new("list_commands");
644    /// c.set_args(&gtp::entity(|eb| eb.v((19, 19))));
645    /// ```
646    pub fn set_args(&mut self, args: &Entity) {
647        self.args = Some(args.clone());
648    }
649
650    /// Generates a String representation of the GTP command.
651    pub fn to_string(&self) -> String {
652        let mut out = String::from("");
653        if self.id.is_some() {
654            out += &format!("{}", self.id.unwrap());
655            out += " ";
656        }
657        out += &self.name;
658
659        if self.args.is_some() {
660            out += " ";
661            out += &self.args.as_ref().unwrap().to_string();
662        }
663        out += "\n";
664        out
665    }
666
667    /// Generates a byte vector representation of the GTP command,
668    /// ready to be sent to another process.
669    #[allow(dead_code)]
670    pub fn to_bytes(&self) -> Vec<u8> {
671        Vec::from(self.to_string().as_bytes())
672    }
673}
674
675/// Represents a GTP response from the GTP engine.
676#[derive(Debug, Clone, PartialEq)]
677pub enum Response {
678    Error((Option<u32>, String)),
679    Result((Option<u32>, String)),
680}
681
682#[derive(Debug)]
683pub enum ResponseParseError {
684    NoInput,
685    BadEntityInput,
686    BadResponse
687}
688
689impl Response {
690    /// Returns the complete response text, which you may feed into EntityParser.
691    ///
692    /// See also the `Response::entities` method.
693    pub fn text(&self) -> String {
694        match self {
695            Response::Error((_, t))  => t.clone(),
696            Response::Result((_, t)) => t.clone(),
697        }
698    }
699
700    /// Returns the ID of the response. Returns 0 if no
701    /// ID was submitted.
702    pub fn id_0(&self) -> u32 {
703        match self {
704            Response::Error((None, _))          => 0,
705            Response::Result((None, _))         => 0,
706            Response::Error((Some(id), _))      => *id,
707            Response::Result((Some(id), _))     => *id,
708        }
709    }
710
711    /// Parses entities from a Response
712    ///
713    /// ```
714    /// let mut rp = gtp::ResponseParser::new();
715    /// rp.feed("= 10 w H6\n\n");
716    /// let entity_vec =
717    ///     rp.get_response().unwrap()
718    ///       .entities(|ep| ep.i().mv())
719    ///       .unwrap();
720    /// assert_eq!(format!("{:?}", entity_vec),
721    ///            "[Int(10), Move((W, (8, 6)))]");
722    ///
723    /// if let gtp::Entity::Int(i) = entity_vec[0] {
724    ///     assert_eq!(i, 10);
725    /// }
726    /// ```
727    ///
728    /// Here is an example for how to read a variable length list:
729    ///
730    /// ```
731    /// let mut rp = gtp::ResponseParser::new();
732    /// rp.feed("= A\nB\nC\nD\nE\n\n");
733    /// let entity_vec =
734    ///     rp.get_response().unwrap()
735    ///       .entities(|ep| { while !ep.is_eof() { ep.s(); }; ep })
736    ///       .unwrap();
737    /// assert_eq!(format!("{:?}", entity_vec),
738    ///            "[String(\"A\"), String(\"B\"), String(\"C\"), String(\"D\"), String(\"E\")]");
739    /// ```
740    pub fn entities<T>(&self, parse_fn: T) -> Result<Vec<Entity>, ResponseParseError>
741        where T: Fn(&mut EntityParser) -> &mut EntityParser  {
742
743        let response = match self {
744            Response::Result((_, res)) => res.to_string(),
745            Response::Error((_, res))  => res.to_string(),
746        };
747
748        let mut ep = EntityParser::new(&response);
749        parse_fn(&mut ep);
750        if ep.had_parse_error() {
751            return Err(ResponseParseError::BadEntityInput);
752        }
753        Ok(ep.result().unwrap())
754    }
755}
756
757/// A parser for a GTP response.
758#[derive(Debug, Clone, PartialEq, Default)]
759pub struct ResponseParser {
760    buffer:     String,
761}
762
763/// Error for the ResponseParser.
764#[derive(Debug, Clone, PartialEq)]
765pub enum ResponseError {
766    IncompleteResponse,
767    BadResponse(String),
768}
769
770fn refine_input(s: String) -> String {
771    let mut ret : String =
772        s.chars()
773         .filter(|c| *c != '\r')
774         .map(|c| if c == '\x09' { ' ' } else { c })
775         .skip_while(|c| *c == '\n' || *c == ' ' || *c == '\x09')
776         .collect();
777
778    loop {
779        let comment_pos = ret.find('#');
780        if comment_pos.is_some() {
781            let end_comment_pos = (&ret[comment_pos.unwrap()..]).find('\n');
782            if end_comment_pos.is_some() {
783                ret = String::from(&ret[..comment_pos.unwrap()])
784                      + &ret[comment_pos.unwrap() + end_comment_pos.unwrap() + 1..];
785            } else {
786                break;
787            }
788        } else {
789            break;
790        }
791    }
792
793    ret
794}
795
796impl ResponseParser {
797    /// Constructs a new GTP engine response parser.
798    ///
799    /// ```
800    /// let mut rp = gtp::ResponseParser::new();
801    /// rp.feed("= ok\n\n");
802    /// let s = rp.get_response().unwrap();
803    /// assert_eq!(format!("{:?}", s), "Result((None, \"ok\"))");
804    /// ```
805    pub fn new() -> ResponseParser {
806        ResponseParser::default()
807    }
808
809    /// Feed the response text to the parser.
810    pub fn feed(&mut self, s: &str) {
811        self.buffer += s;
812    }
813
814    /// Tries to read the response from the until now feeded input.
815    ///
816    /// Returns `Ok(None)` if no response is available yet.
817    /// Returns an error if the response is malformed.
818    /// Returns the Ok([`Response`](enum.Response.html)) if one could be read.
819    #[allow(unused_assignments, clippy::collapsible_if)]
820    pub fn get_response(&mut self) -> Result<Response, ResponseError> {
821        self.buffer = refine_input(self.buffer.to_string());
822        if self.buffer.is_empty() { return Err(ResponseError::IncompleteResponse); }
823
824        let is_error = self.buffer.chars().nth(0).unwrap() != '=';
825
826        let mut id_str   = String::from("");
827        let mut response = String::from("");
828
829        let mut read_id =
830            !(   self.buffer.len() > 1
831              && self.buffer.chars().nth(1).unwrap() == ' ');
832
833        let mut found_start      = false;
834        let mut found_end        = false;
835        let mut last_was_newline = false;
836        let mut skip_count       = 1;
837
838        for c in self.buffer.chars().skip(1) {
839            skip_count += 1;
840
841            if read_id {
842                match c {
843                    c if c.is_ascii_digit() => {
844                        id_str.push(c);
845                    },
846                    ' ' => {
847                        found_start = true;
848                        read_id     = false;
849                    },
850                    _ => { return Err(ResponseError::BadResponse(self.buffer.to_string())); }
851                }
852            } else if !found_start {
853                if c == ' ' {
854                    found_start = true;
855                } else {
856                    return Err(ResponseError::BadResponse(self.buffer.to_string()));
857                }
858            } else {
859                if c == '\n' {
860                    if last_was_newline {
861                        found_end = true;
862                        break;
863                    } else {
864                        last_was_newline = true;
865                    }
866                } else {
867                    if last_was_newline {
868                        response.push('\n');
869                    }
870                    last_was_newline = false;
871                    response.push(c);
872                }
873            }
874        }
875
876        if found_end {
877            self.buffer = self.buffer.chars().skip(skip_count).collect();
878        } else {
879            return Err(ResponseError::IncompleteResponse);
880        }
881
882        let id = if !id_str.is_empty() {
883            if let Ok(cn) = u32::from_str_radix(&id_str, 10) {
884                Some(cn)
885            } else {
886                None
887            }
888        } else {
889            None
890        };
891
892        if is_error {
893            Ok(Response::Error((id, response)))
894        } else {
895            Ok(Response::Result((id, response)))
896        }
897    }
898}
899
900#[cfg(test)]
901mod tests {
902    use super::*;
903
904    #[test]
905    fn check_printing() {
906        assert_eq!(Entity::Int(10).to_string(),                      "10");
907        assert_eq!(Entity::Float(10.12).to_string(),                 "10.12");
908        assert_eq!(Entity::String(String::from("Test")).to_string(), "Test");
909        assert_eq!(Entity::Vertex((-1, -1)).to_string(),             "pass");
910        assert_eq!(Entity::Vertex((1, 1)).to_string(),               "A1");
911        assert_eq!(Entity::Vertex((19, 19)).to_string(),             "T19");
912        assert_eq!(Entity::Vertex((8, 19)).to_string(),              "H19");
913        assert_eq!(Entity::Vertex((9, 19)).to_string(),              "J19");
914        assert_eq!(Entity::Color(Color::W).to_string(),              "w");
915        assert_eq!(Entity::Color(Color::B).to_string(),              "b");
916        assert_eq!(Entity::Move((Color::B, (0, 0))).to_string(),     "b pass");
917        assert_eq!(Entity::Move((Color::W, (0, 0))).to_string(),     "w pass");
918        assert_eq!(Entity::Move((Color::B, (8, 1))).to_string(),     "b H1");
919        assert_eq!(Entity::Move((Color::W, (9, 1))).to_string(),     "w J1");
920        assert_eq!(Entity::Move((Color::B, (19, 1))).to_string(),    "b T1");
921        assert_eq!(Entity::Move((Color::W, (19, 19))).to_string(),   "w T19");
922        assert_eq!(Entity::Boolean(true).to_string(),                "true");
923        assert_eq!(Entity::Boolean(false).to_string(),               "false");
924        assert_eq!(Entity::List(vec![Entity::Int(1), Entity::Int(2)]).to_string(),
925                   "1 2");
926        assert_eq!(Entity::List(vec![
927                        Entity::List(vec![Entity::Int(1), Entity::Int(2)]),
928                        Entity::List(vec![Entity::Int(3), Entity::Int(4)])]).to_string(),
929                   "1 2\n3 4");
930    }
931
932    #[test]
933    fn check_entity_builder() {
934        assert_eq!(entity(|eb| eb.i(10)).to_string(),           "10");
935        assert_eq!(entity(|eb| eb.f(10.12)).to_string(),        "10.12");
936        assert_eq!(entity(|eb| eb.s("ok")).to_string(),         "ok");
937        assert_eq!(entity(|eb| eb.v_pass()).to_string(),        "pass");
938        assert_eq!(entity(|eb| eb.v((19, 19))).to_string(),     "T19");
939        assert_eq!(entity(|eb| eb.bool(false)).to_string(),     "false");
940        assert_eq!(entity(|eb| eb.w()).to_string(),             "w");
941        assert_eq!(entity(|eb| eb.b()).to_string(),             "b");
942        assert_eq!(entity(|eb| eb.color(true)).to_string(),     "w");
943        assert_eq!(entity(|eb| eb.color(false)).to_string(),    "b");
944        assert_eq!(entity(|eb| eb.mv_w((8, 8))).to_string(),    "w H8");
945        assert_eq!(entity(|eb| eb.mv_b((8, 8))).to_string(),    "b H8");
946        assert_eq!(entity(|eb| eb.mv(true, (8, 8))).to_string(),"w H8");
947        assert_eq!(entity(|eb| eb.mv_w((8, 8)).mv_b((9, 9)).list()).to_string(),
948                   "w H8 b J9");
949    }
950
951    #[test]
952    fn check_entity_parser() {
953        let mut ep = EntityParser::new("10 10.2 ok WHite t19 false");
954        ep.i().f().s().mv().bool();
955        let res = ep.result().unwrap();
956        assert_eq!(res[0].to_string(), "10");
957        assert_eq!(res[1].to_string(), "10.2");
958        assert_eq!(res[2].to_string(), "ok");
959        assert_eq!(res[3].to_string(), "w T19");
960        assert_eq!(res[4].to_string(), "false");
961    }
962
963    #[test]
964    fn check_eof() {
965        let mut ep = EntityParser::new("t19 b10 a1 d2");
966        while !ep.is_eof() {
967            ep.vertex();
968        }
969        let res = ep.result().unwrap();
970        assert_eq!(res[3].to_string(), "D2");
971    }
972
973    #[test]
974    fn check_build_command() {
975        let mut c = Command::new("list_commands");
976        c.args(|eb| eb.i(10).f(10.20).s("OK").list());
977        assert_eq!(c.to_string(), "list_commands 10 10.2 OK\n");
978
979        assert_eq!(
980            Command::new_with_args("boardsize", |eb| eb.i(9)).to_string(),
981            "boardsize 9\n");
982    }
983
984    #[test]
985    fn check_setid_command() {
986        let mut c = Command::new("list_commands");
987        c.set_id(12);
988        assert_eq!(c.to_string(), "12 list_commands\n");
989    }
990
991    fn must_parse(s: &str) -> Response {
992        let mut rp = ResponseParser::new();
993        rp.feed(s);
994        let s = rp.get_response().unwrap();
995        s
996    }
997
998    #[test]
999    fn check_parser() {
1000        {
1001            let mut rp = ResponseParser::new();
1002            rp.feed("= ok\n\n");
1003            let s = rp.get_response().unwrap();
1004            assert_eq!(format!("{:?}", s), "Result((None, \"ok\"))");
1005        }
1006
1007        {
1008            let mut rp = ResponseParser::new();
1009            rp.feed("= ok\n\n");
1010            rp.feed("= \n\n");
1011
1012            assert_eq!(rp.get_response().unwrap().text(), "ok");
1013        }
1014
1015        {
1016            let mut rp = ResponseParser::new();
1017            rp.feed("= ok\n");
1018
1019            assert!(rp.get_response().is_err());
1020        }
1021
1022        let res = must_parse("= ok\nfoobar\n\n");
1023        assert_eq!(res.text(), "ok\nfoobar");
1024
1025        assert_eq!(format!("{:?}", must_parse("=10 ok\n\n")),
1026                   "Result((Some(10), \"ok\"))");
1027
1028        assert_eq!(format!("{:?}", must_parse("#\n=10 ok\n\n")),
1029                   "Result((Some(10), \"ok\"))");
1030
1031        assert_eq!(format!("{:?}", must_parse("= ok\n\n")),
1032                   "Result((None, \"ok\"))");
1033
1034        assert_eq!(format!("{:?}", must_parse("= \n\n")),
1035                   "Result((None, \"\"))");
1036
1037        assert_eq!(format!("{:?}", must_parse("= \na\nb\nc\n\n")),
1038                   "Result((None, \"\\na\\nb\\nc\"))");
1039
1040        assert_eq!(format!("{:?}", must_parse("= foo # all ok\n\n\n")),
1041                   "Result((None, \"foo \"))");
1042
1043        assert_eq!(format!("{:?}", must_parse("= \na\nb fooo #fewiofw jfw\nc\n\n")),
1044                   "Result((None, \"\\na\\nb fooo c\"))");
1045    }
1046
1047}