libgtp 0.1.2

A library implmenting the gtp protocol
Documentation
use std::sync::Arc;
use std::sync::Mutex;
use std::collections::VecDeque;
use std::io::{ BufReader, BufRead, BufWriter, Write };
use std::process::ChildStdin;

#[derive(Debug)]
pub struct Engine {
    child: std::process::Child,
    stdin: BufWriter<ChildStdin>,
    incoming_lines: Arc<Mutex<VecDeque<String>>>,
    analyze_line: Arc<Mutex<String>>,
    pub is_ready: Arc<Mutex<bool>>,
}

impl Engine {
    pub fn new(name: &str, args: &[&str]) -> Result<Self, std::io::Error> {
        let mut child = std::process::Command::new(name)
            .args(args)
            .stdin(std::process::Stdio::piped())
            .stdout(std::process::Stdio::piped())
            .stderr(std::process::Stdio::piped())
            .spawn()?;

        let stdin  = BufWriter::new(child.stdin.take().unwrap());
        Ok(Self {
            child,
            stdin,
            incoming_lines: Arc::new(Mutex::new(VecDeque::new())),
            analyze_line: Arc::new(Mutex::new("".to_string())),
            is_ready: Arc::new(Mutex::new(false)),
        })
    }

    pub fn start(mut self) -> Self {
        let is_ready = self.is_ready.clone();

        let stdout = BufReader::new(self.child.stdout.take().unwrap());
        let lines = self.incoming_lines.clone();
        let line = self.analyze_line.clone();
        let stderr = BufReader::new(self.child.stderr.take().unwrap());
        std::thread::spawn(move || {
            for l in stderr.lines().map(|x| x.unwrap()) {
                eprintln!("{}", l);
                if l == "GTP ready, beginning main protocol loop".to_string() {
                    let mut is_ready = is_ready.lock().unwrap();
                    *is_ready = true;
                }
            }
        });
        std::thread::spawn(move || {
            for l in stdout.lines().map(|x| x.unwrap()) {
                if !l.is_empty() {
                    if l.as_bytes()[0] == b'i' {
                        let mut line = line.lock().unwrap();
                        *line = l;
                    } else {
                        let mut lines = lines.lock().unwrap();
                        println!("{}", l);
                        lines.push_back(l);
                    }
                }
            }
        });

        self
    }

    pub fn kill(mut self) {
        self.child.kill().unwrap();
    }

    pub fn read_line(&mut self) -> String {
        while self.incoming_lines.lock().unwrap().front().is_none() {}
        let s = self.incoming_lines.lock().unwrap().pop_front().unwrap();
        s
    }

    pub fn read_info(&self) -> String {
        self.analyze_line.lock().unwrap().clone()
    }

    pub fn discard_info(&mut self) {
        let mut line = self.analyze_line.lock().unwrap();
        *line = "".to_string();
    }
}

impl Write for Engine {
    fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
        while *self.is_ready.lock().unwrap() != true {}

        println!("{}", String::from_utf8(buf.to_vec()).unwrap());
        self.stdin.write(buf)
    }

    fn flush(&mut self) -> Result<(), std::io::Error> {
        self.stdin.flush()
    }
}