easy-smt 0.3.2

Easily interact with an smt solver as a subprocess
Documentation
use crate::sexpr::{Arena, ParseError, Parser, SExpr};
use std::ffi;
use std::io::{self, BufRead};
use std::process;

pub(crate) struct Solver {
    handle: process::Child,
    stdin: io::BufWriter<process::ChildStdin>,
    stdout: io::Lines<io::BufReader<process::ChildStdout>>,
    replay_file: io::BufWriter<Box<dyn io::Write + Send>>,
    parser: Parser,
}

impl Solver {
    pub fn new(
        program: ffi::OsString,
        args: Vec<ffi::OsString>,
        replay_file: Box<dyn io::Write + Send>,
    ) -> io::Result<Self> {
        let mut handle = process::Command::new(program)
            .args(args)
            .stdin(process::Stdio::piped())
            .stdout(process::Stdio::piped())
            .spawn()?;
        let stdin = handle.stdin.take().unwrap();
        let stdout = handle.stdout.take().unwrap();

        Ok(Self {
            handle,
            stdin: io::BufWriter::new(stdin),
            stdout: io::BufReader::new(stdout).lines(),
            replay_file: io::BufWriter::new(replay_file),
            parser: Parser::new(),
        })
    }

    pub fn send(&mut self, arena: &Arena, expr: SExpr) -> io::Result<()> {
        use io::Write;
        log::trace!("-> {}", arena.display(expr));
        writeln!(self.replay_file, "{}", arena.display(expr))?;
        self.replay_file.flush()?;
        writeln!(self.stdin, "{}", arena.display(expr))?;
        self.stdin.flush()
    }

    pub fn recv(&mut self, arena: &Arena) -> io::Result<SExpr> {
        self.parser.reset();

        for line in self.stdout.by_ref() {
            let line = line?;
            log::trace!("<- {}", line);
            match self.parser.parse(arena, &line) {
                Ok(res) => return Ok(res),
                Err(ParseError::More) => continue,
                Err(ParseError::Message(msg)) => {
                    log::error!("Failed to parse: {line}");
                    log::error!("Parse error: {msg}");
                }
            }
        }

        Err(std::io::Error::new(
            std::io::ErrorKind::Other,
            "Failed to parse solver output",
        ))
    }

    pub fn ack_command(&mut self, arena: &Arena, success: SExpr, c: SExpr) -> io::Result<()> {
        self.send(arena, c)?;
        let resp = self.recv(arena)?;
        if resp == success {
            Ok(())
        } else {
            Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Unexpected result from solver: {}", arena.display(resp)),
            ))
        }
    }
}

impl Drop for Solver {
    fn drop(&mut self) {
        let _ = self.handle.kill();
        let _ = self.handle.wait();
    }
}