use es::traits::*;
use std::process;
use std::env;
use std::fs;
use std::path::{Path,PathBuf};
use std::collections::HashMap;
use std::io::Write;
use crate::crate_utils;
use crate::meta;
use crate_utils::UNSTABLE;
use crate::state::State;
const STATIC_CACHE: &str = "static-cache";
const DYNAMIC_CACHE: &str = "dy-cache";
const PRELUDE: &str = "
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![allow(unused_macros)]
use std::{fs,io,env};
use std::fs::File;
use std::io::prelude::*;
use std::path::{PathBuf,Path};
use std::collections::HashMap;
use std::time::Duration;
use std::thread;
macro_rules! debug {
($x:expr) => {
println!(\"{} = {:?}\",stringify!($x),$x);
}
}
";
const KITCHEN_SINK: &str = "
chrono
regex
serde_json
serde_yaml
";
pub fn quote(s: String) -> String {
if cfg!(windows) {
s.replace("\'","\"")
} else {
s
}
}
pub fn runner_directory() -> PathBuf {
let mut runner = crate_utils::cargo_home().join(".runner");
if *UNSTABLE {
runner.push("unstable");
}
runner
}
pub fn cargo(args: &[&str]) -> bool {
let res = process::Command::new("cargo")
.args(args)
.status()
.or_die("can't run cargo");
res.success()
}
pub fn cargo_build(release: bool) -> Option<String> {
use process::Stdio;
use std::io::BufReader;
use std::io::prelude::*;
let mut c = process::Command::new("cargo");
c.arg("build");
if release {
c.arg("--release");
}
c.stdout(Stdio::piped());
c.arg("--message-format").arg("json");
let mut res = c.spawn().or_die("can't run cargo");
let inb = BufReader::new(res.stdout.take().unwrap());
let mut out = String::new();
for line in inb.lines() {
if let Ok(line) = line {
if line.starts_with('{') {
out += &line;
out.push('\n');
} else {
println!("{}",line);
}
}
}
if res.wait().or_die("cargo build error").success() {
Some(out)
} else {
None
}
}
pub fn static_cache_dir() -> PathBuf {
runner_directory().join(STATIC_CACHE)
}
pub fn get_metadata() -> meta::Meta {
let static_cache = static_cache_dir();
if meta::Meta::exists(&static_cache) {
meta::Meta::new_from_file(&static_cache)
} else {
es::quit("please build the static cache with `runner --add <crate>...` first");
}
}
pub fn static_cache_dir_check() -> PathBuf {
let static_cache = static_cache_dir();
if ! static_cache.exists() {
es::quit("please build the static cache with `runner --add <crate>...` first");
}
static_cache
}
pub fn build_static_cache() -> bool {
use crate::meta::*;
let mut m = Meta::new();
match cargo_build(false) {
None => return false,
Some(s) => m.debug(s)
}
match cargo_build(true) {
None => return false,
Some(s) => m.release(s)
}
m.update(&static_cache_dir());
cargo(&["doc"])
}
pub fn create_static_cache(crates: &[String]) {
use std::io::prelude::*;
let static_cache = static_cache_dir();
let exists = static_cache.exists();
let crates = if crates.len() == 1 && crates[0] == "kitchen-sink" {
KITCHEN_SINK.split_whitespace().map(|s| s.into()).collect()
} else {
crates.to_vec()
};
let mut home = runner_directory();
env::set_current_dir(&home).or_die("cannot change to home directory");
let mdata = if ! exists {
if ! cargo(&["new","--bin",STATIC_CACHE]) {
es::quit("cannot create static cache");
}
None
} else {
Some(get_metadata())
};
let check_crate = |s: &str| if let Some(m) = &mdata {
m.is_crate_present(s)
} else {
false
};
let crates_vs = crates.iter().filter_map(|c| {
if let Some(idx) = c.find('=') {
let (name,vs) = (&c[0..idx], &c[(idx+1)..]);
Some((name.to_string(),vs.to_string(),true))
} else {
if let Some((name,path)) = maybe_cargo_dir(&c) {
if check_crate(&name) {
None
} else {
Some((name, path.to_str().unwrap().to_string(),false))
}
} else { if check_crate(c) {
None
} else {
Some((c.to_string(), '*'.to_string(),true))
}
}
}
}).to_vec();
if crates_vs.len() == 0 {
return;
}
home.push(STATIC_CACHE);
env::set_current_dir(&home).or_die("could not change to static cache directory");
let tmpfile = env::temp_dir().join("Cargo.toml");
fs::copy("Cargo.toml",&tmpfile).or_die("cannot back up Cargo.toml");
{
let mut deps = fs::OpenOptions::new().append(true)
.open("Cargo.toml").or_die("could not append to Cargo.toml");
for (name,vs,semver) in crates_vs {
if semver {
write!(deps,"{}=\"{}\"\n",name,vs)
} else {
write!(deps,"{}={{path=\"{}\"}}\n",name,vs)
}.or_die("could not modify Cargo.toml");
}
}
if ! build_static_cache() {
println!("Error occurred - restoring Cargo.toml");
fs::copy(&tmpfile,"Cargo.toml").or_die("cannot restore Cargo.toml");
}
}
fn maybe_cargo_dir(name: &str) -> Option<(String,PathBuf)> {
let path = Path::new(name);
if ! path.exists() || ! path.is_dir() {
return None;
}
let full_path = path.canonicalize().or_die("bad path, man!");
if let Ok((full_path,cargo_toml)) = crate_utils::cargo_dir(&full_path) {
let name = crate_utils::crate_info(&cargo_toml).name;
Some((name,full_path))
} else {
None
}
}
pub fn get_prelude() -> String {
let home = runner_directory();
let pristine = ! home.is_dir();
if pristine {
fs::create_dir_all(&home).or_die("cannot create runner directory");
}
let prelude = home.join("prelude");
let bin = home.join("bin");
if pristine {
fs::write(&prelude,PRELUDE).or_die("cannot write prelude");
fs::create_dir(&home.join(DYNAMIC_CACHE)).or_die("cannot create dynamic cache");
}
if pristine || ! bin.is_dir() {
fs::create_dir(&bin).or_die("cannot create output directory");
}
fs::read_to_string(&prelude).or_die("cannot read prelude")
}
pub fn get_cache(state: &State) -> PathBuf {
let mut home = runner_directory();
if state.build_static {
home.push(STATIC_CACHE);
home.push("target");
home.push(if state.optimize {"release"} else {"debug"});
home.push("deps");
} else {
home.push(DYNAMIC_CACHE);
};
home
}
pub fn add_aliases(aliases: Vec<String>) {
if aliases.len() == 0 { return; }
let alias_file = runner_directory().join("alias");
let mut f = if alias_file.is_file() {
fs::OpenOptions::new().append(true).open(&alias_file)
} else {
fs::File::create(&alias_file)
}.or_die("cannot open runner alias file");
for crate_alias in aliases {
write!(f,"{}\n",crate_alias).or_die("cannot write to runner alias file");
}
}
pub fn get_aliases() -> HashMap<String,String> {
let alias_file = runner_directory().join("alias");
if ! alias_file.is_file() { return HashMap::new(); }
let contents = fs::read_to_string(&alias_file).or_die("cannot read alias file");
contents.lines()
.filter_map(|s| s.split_at_delim('=').trim()) .to_map()
}