zargo 0.1.1

The sysroot manager that lets you build and customize `std`
Documentation
use std::{
    fs::{self, File},
    io::Write,
    path::Path,
};

use toml::Value;
use walkdir::WalkDir;

use crate::errors::*;

pub fn cp_r(src: &Path, dst: &Path) -> Result<()> {
    for e in WalkDir::new(src) {
        // This is only an error when there's some sort of intermittent IO error
        // during iteration.
        // see https://doc.rust-lang.org/std/fs/struct.ReadDir.html
        let e = e.chain_err(|| {
            format!(
                "intermittent IO error while iterating directory `{}`",
                src.display()
            )
        })?;

        let src_file = e.path();
        let relative_path = src_file.strip_prefix(src).chain_err(|| {
            format!(
                "Could not retrieve relative path of child directory or \
                 file `{}` with regards to parent directory `{}`",
                src_file.display(),
                src.display()
            )
        })?;

        let dst_file = dst.join(relative_path);

        let ft = e.file_type();
        if ft.is_dir() {
            // `create_dir_all` already guarantees the full hierarchy
            fs::create_dir_all(&dst_file)
                .chain_err(|| format!("Could not create directory `{}`", dst_file.display()))?;
        } else {
            fs::copy(src_file, &dst_file).chain_err(|| {
                format!(
                    "copying files from `{}` to `{}` failed",
                    src_file.display(),
                    dst_file.display()
                )
            })?;
        }
    }

    Ok(())
}

pub fn mkdir(path: &Path) -> Result<()> {
    fs::create_dir(path).chain_err(|| format!("couldn't create directory {}", path.display()))
}

/// Parses `path` as TOML
pub fn parse(path: &Path) -> Result<Value> {
    Ok(toml::from_str(&read(path)?)
        .map_err(|e| format!("{} is not valid TOML: {}", path.display(), e))?)
}

pub fn read(path: &Path) -> Result<String> {
    fs::read_to_string(path).chain_err(|| format!("couldn't read {}", path.display()))
}

/// Search for `file` in `path` and its parent directories
pub fn search<'p>(mut path: &'p Path, file: &str) -> Option<&'p Path> {
    loop {
        if path.join(file).exists() {
            return Some(path);
        }

        if let Some(p) = path.parent() {
            path = p;
        } else {
            return None;
        }
    }
}

pub fn write(path: &Path, contents: &str) -> Result<()> {
    let p = path.display();
    File::create(path)
        .chain_err(|| format!("couldn't open {}", p))?
        .write_all(contents.as_bytes())
        .chain_err(|| format!("couldn't write to {}", p))
}