use proc_macro2::TokenStream;
use quote::quote;
use std::io::Result;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::sync::Mutex;
use std::{env, fs, io};
#[non_exhaustive]
pub struct Sandbox {
lock: Mutex<()>,
root_dir: PathBuf,
}
impl Sandbox {
pub fn new(uuid: &str) -> Result<Self> {
let out_dir = env!("OUT_DIR");
let mut root_dir = PathBuf::from(out_dir);
root_dir.push(uuid);
Command::new("mkdir")
.args(&["-p", root_dir.to_str().unwrap()])
.output()?;
Command::new("cargo")
.current_dir(&root_dir)
.args(&["new", "sandbox"])
.output()?;
root_dir.push("sandbox");
Ok(Sandbox {
root_dir,
lock: Mutex::new(()),
})
}
pub fn deps(self, deps: &[&str]) -> Result<Self> {
let Self { lock, root_dir } = &self;
let _ = lock.lock().unwrap();
for dep in deps {
Command::new("cargo")
.args(&["add", dep])
.current_dir(&root_dir)
.output()?;
}
Ok(self)
}
pub fn eval<T: FromStr + ToString>(self, expr: TokenStream) -> Result<T> {
let Self { lock, root_dir } = self;
let _ = lock.lock().unwrap();
let wrapper = quote! {
use std::io::prelude::*;
fn main() -> std::io::Result<()> {
let mut file = std::fs::File::create("output")?;
let output = { #expr }.to_string();
file.write_all(output.as_bytes())?;
Ok(())
}
};
fs::write(root_dir.join("src/main.rs"), wrapper.to_string())?;
Command::new("cargo")
.arg("run")
.current_dir(&root_dir)
.output()?;
let output = fs::read_to_string(root_dir.join("output"))?
.parse()
.or(Err(io::ErrorKind::Other))?;
fs::remove_file(root_dir.join("output"))?;
Ok(output)
}
}