extern crate docopt;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate unshare;
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::exit;
use docopt::Docopt;
use toml::de::from_str;
fn main() {
let args = Arguments::load();
if args.flag_default_config {
print!("{}", DEFAULT_CONFIG);
exit(0);
}
args.into_command().exec();
}
const USAGE: &'static str = "
Usage:
isolate [--config-file <file>] <program> [<args>...]
isolate [-v | -h | -d]
Options:
-f <file>, --config-file <file> Location of configuration file to use.
-h, --help Show this help.
-v, --version Show the version.
-d, --default-config Dumpt the default configuration to stdout.
";
#[derive(Deserialize)]
struct Arguments {
flag_config_file: Option<String>,
flag_default_config: bool,
arg_program: String,
arg_args: Vec<String>
}
impl Arguments {
fn load() -> Arguments {
Docopt::new(USAGE)
.unwrap_or_else(|e| e.exit())
.help(true)
.version(Some(version()))
.deserialize()
.unwrap_or_else(|e| e.exit())
}
fn into_command(self) -> Command {
let config = self.config();
Command::new(self.arg_program, self.arg_args, config)
}
fn config(&self) -> Configuration {
let text = if let Some(ref path) = self.find_config_path() {
let mut file = File::open(path).expect("could not open configuration file");
let mut text = String::new();
file.read_to_string(&mut text).expect("could not read configuration file");
text
} else {
DEFAULT_CONFIG.to_string()
};
from_str(&text).expect("could not parse configuration")
}
fn find_config_path(&self) -> Option<String> {
if let Some(ref path) = self.flag_config_file {
Some(path.clone())
} else {
let paths = Arguments::default_config_paths();
for path in paths {
if Path::new(&path).exists() {
return Some(path)
}
}
None
}
}
fn default_config_paths() -> Vec<String> {
let mut paths = vec![
"isolate.toml".to_string(),
".isolate.toml".to_string()
];
if let Ok(path) = env::var("HOME") {
paths.push(format!("{}/.config/isolate.toml", path));
paths.push(format!("{}/.isolate.toml", path));
}
paths.push("/etc/isolate.toml".to_string());
paths
}
}
const DEFAULT_CONFIG: &'static str = include_str!("isolate.toml");
#[derive(Deserialize)]
struct Configuration {
}
struct Command {
program: String,
arguments: Vec<String>,
config: Configuration,
}
impl Command {
fn new(program: String, args: Vec<String>, config: Configuration) -> Command {
Command {
program: program,
arguments: args,
config: config,
}
}
fn exec(&self) {
unshare::Command::new(&self.program)
.args(&self.arguments)
.spawn()
.expect("unable to spawn process")
.wait()
.expect("error in child process");
}
}
fn version() -> String {
format!(
"{} - {}\n{}\n\n{}",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_AUTHORS"),
env!("CARGO_PKG_DESCRIPTION"),
)
}