use log::{debug, error, log_enabled, Level};
use std::io::{self, BufRead, BufReader, Cursor};
use std::path::Path;
use std::process::{Child, Command, Stdio};
use std::{cmp, env};
use crate::bson::BsonReader;
use crate::tool::connection::PluginConnection;
use crate::tool::{PluginError, PluginResult};
use crate::PluginInfo;
fn log_as_error(bytes: Vec<u8>) -> io::Result<()> {
let mut reader = BufReader::new(Cursor::new(bytes));
let mut line_buf = vec![];
loop {
line_buf.clear();
let n = reader.read_until(0x0A, &mut line_buf)?;
if n > 0 {
let line = String::from_utf8_lossy(&line_buf);
error!("{}", line.trim());
} else {
break;
}
}
Ok(())
}
pub struct Plugin<'a>(&'a Path);
impl<'a> Plugin<'a> {
pub fn new(binary: &Path) -> Plugin {
Plugin(binary)
}
pub fn open(&self, name: &str, verbose: u8) -> PluginResult<PluginConnection> {
let child = self.new_child(Stdio::piped(), &Self::make_args("open", name, verbose, &[]))?;
Ok(PluginConnection::new(child))
}
pub fn create(
&self,
name: &str,
verbose: u8,
extra_args: &[String],
) -> PluginResult<PluginConnection> {
let child = self.new_child(
Stdio::piped(),
&Self::make_args("create", name, verbose, extra_args),
)?;
Ok(PluginConnection::new(child))
}
pub fn info(&self) -> PluginResult<PluginInfo> {
let child = self.new_child(Stdio::null(), &["info", "--format=bson"])?;
let output = child.wait_with_output()?;
if output.status.success() {
let mut reader = BsonReader::new(Cursor::new(output.stdout));
match reader.read()? {
Some(info) => {
debug!("plugin infos: {:?}", info);
Ok(info)
}
None => Err(PluginError::ShortPluginInfo),
}
} else {
if log_enabled!(Level::Error) {
log_as_error(output.stderr)?;
}
Err(PluginError::BadPluginInfo(output.status))
}
}
fn make_args<'b>(
command: &'b str,
name: &'b str,
verbose: u8,
extra_args: &'b [String],
) -> Vec<&'b str> {
let mut args = vec![command, name];
extra_args
.iter()
.map(|s| s.as_str())
.for_each(|s| args.push(s));
let max = cmp::min(verbose, 3);
(0..max).for_each(|_| args.push("-v"));
args
}
fn new_child(&self, stdin: Stdio, args: &[&str]) -> PluginResult<Child> {
debug!("executing {} with {:?}", self.0.display(), args);
let mut command = Command::new(self.0);
command
.args(args)
.stdin(stdin)
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(value) = env::var_os("RUST_BACKTRACE") {
command.env("RUST_BACKTRACE", value);
}
Ok(command.spawn()?)
}
}