use strfmt;
use serde_json;
use std::path::MAIN_SEPARATOR;
lazy_static! {
static ref MAIN_SEPARATOR_STR: String = MAIN_SEPARATOR.to_string();
}
use dev_prefix::*;
use super::types::*;
use itertools::{Itertools, EitherOrBoth as EoB};
pub fn do_strfmt(s: &str, vars: &HashMap<String, String>, fpath: &PathBuf) -> Result<String> {
strfmt::strfmt(s, vars).chain_err(|| format!("ERROR at {}: {}", fpath.display(), s.to_string()))
}
pub fn strfmt_ignore_missing<T: fmt::Display>(fmtstr: &str,
vars: &HashMap<String, T>)
-> strfmt::Result<String> {
let formatter = |mut fmt: strfmt::Formatter| match vars.get(fmt.key) {
Some(v) => fmt.str(v.to_string().as_str()),
None => fmt.skip(),
};
strfmt::strfmt_map(fmtstr, &formatter)
}
pub fn artifacts_to_json(artifacts: &Artifacts, names: Option<&[ArtNameRc]>) -> String {
let out_arts: Vec<_> = if let Some(names) = names {
names.iter()
.map(|n| artifacts.get(n).unwrap().to_data(n))
.collect()
} else {
artifacts.iter()
.map(|(n, a)| a.to_data(n))
.collect()
};
let value = serde_json::to_value(out_arts).unwrap();
serde_json::to_string(&value).unwrap()
}
pub fn get_path_str(path: &Path) -> Result<&str> {
match path.to_str() {
Some(p) => Ok(p),
None => Err(ErrorKind::InvalidUnicode(format!("{}", path.display())).into()),
}
}
pub fn find_repo(dir: &Path) -> Option<PathBuf> {
let dir = env::current_dir().unwrap().join(dir);
assert!(dir.is_dir(), "{}", dir.display());
let mut dir = dir.as_path();
fn has_rst_dir(entry: io::Result<fs::DirEntry>) -> bool {
match entry {
Err(_) => false,
Ok(e) => {
let p = e.path();
let fname = p.file_name().unwrap().to_str().unwrap();
fname == ".art" && p.is_dir()
}
}
}
loop {
let mut read_dir = match fs::read_dir(dir) {
Ok(d) => d,
Err(_) => return None,
};
if read_dir.any(has_rst_dir) {
return Some(dir.to_path_buf());
}
dir = match dir.parent() {
Some(d) => d,
None => return None,
};
}
}
pub fn find_and_insert_repo(dir: &Path, repo_map: &mut HashMap<PathBuf, PathBuf>) -> Result<()> {
let mut must_insert = false;
let repo = match repo_map.get(dir) {
Some(r) => r.to_path_buf(),
None => {
let r = match find_repo(dir) {
Some(r) => r,
None => {
let msg = format!("dir is not part of a repo: {}", dir.display());
return Err(ErrorKind::Load(msg).into());
}
};
must_insert = true;
r.to_path_buf()
}
};
if must_insert {
repo_map.insert(dir.to_path_buf(), repo);
}
Ok(())
}
pub fn relative_path(path: &Path, relative_to_dir: &Path) -> PathBuf {
let mut relative = PathBuf::new();
let mut remaining = PathBuf::new();
let mut still_alike = true;
if path == PARENT_PATH.as_path() {
return path.to_path_buf();
}
for zipped in path.components().zip_longest(relative_to_dir.components()) {
if still_alike {
still_alike = match zipped {
EoB::Both(a, b) => a == b, EoB::Left(_) => false, _ => unreachable!("paths have no identical root"),
}
}
if !still_alike {
match zipped {
EoB::Both(a, _) => {
relative.push("..");
remaining.push(a.as_ref());
}
EoB::Left(a) => remaining.push(a.as_ref()),
EoB::Right(_) => relative.push(".."),
}
}
}
relative.extend(remaining.iter());
relative
}
#[test]
fn test_relative_path() {
assert_eq!(relative_path(&PathBuf::from("/foo/bar/txt.t"),
&PathBuf::from("/foo/bar/")),
PathBuf::from("txt.t"));
assert_eq!(relative_path(&PathBuf::from("/foo/bar/baz/txt.t"),
&PathBuf::from("/foo/bar/")),
PathBuf::from("baz/txt.t"));
assert_eq!(relative_path(&PathBuf::from("foo/bar/txt.t"), &PathBuf::from("foo/baz/")),
PathBuf::from("../bar/txt.t"));
assert_eq!(relative_path(&PathBuf::from("/home/user/projects/what/src/foo/bar.txt"),
&PathBuf::from("/home/user/projects/what/reqs/left/right/a/b/c/")),
PathBuf::from("../../../../../../src/foo/bar.txt"));
}
#[cfg(windows)]
pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
let canon = fs::canonicalize(path)?;
let mut path_iter = canon.iter();
let prefix = path_iter.next().unwrap();
let prefix_str = prefix.to_os_string().into_string().unwrap();
let (icky, new_prefix_str) = prefix_str.split_at(4);
assert_eq!(icky, r"\\?\");
let new_prefix = OsString::from(new_prefix_str.to_string());
let mut new_path = PathBuf::from(&new_prefix);
new_path.extend(path_iter);
Ok(new_path)
}
#[cfg(not(windows))]
pub fn canonicalize(path: &Path) -> io::Result<PathBuf> {
fs::canonicalize(path)
}
pub fn convert_path_str(path: &str) -> String {
path.replace("/", &MAIN_SEPARATOR_STR)
}
#[test]
#[cfg(windows)]
fn test_convert_windows() {
let expected = "this\\is\\a\\windows\\path";
assert_eq!(expected, convert_path_str("this/is/a/windows/path"));
}
#[test]
#[cfg(not(windows))]
fn test_convert_posix() {
let expected = "this/is/a/windows/path";
assert_eq!(expected, convert_path_str(expected));
}