1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;

use toml::{Parser, Value};
use walkdir::WalkDir;

use 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 metadata = e.metadata()
            .chain_err(|| format!("Could not retrieve metadata of `{}`", e.path().display()))?;

        if metadata.is_dir() {
            // ensure the destination directory exists
            fs::create_dir_all(&dst_file)
                .chain_err(|| format!("Could not create directory `{}`", dst_file.display()))?;
        } else {
            // else copy the file
            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(Value::Table(Parser::new(&read(path)?).parse().ok_or_else(
        || format!("{} is not valid TOML", path.display()),
    )?))
}

pub fn read(path: &Path) -> Result<String> {
    let mut s = String::new();

    let p = path.display();
    File::open(path)
        .chain_err(|| format!("couldn't open {}", p))?
        .read_to_string(&mut s)
        .chain_err(|| format!("couldn't read {}", p))?;

    Ok(s)
}

/// 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))
}

/// Escapes spaces (` `) in the given input with `%20` or does nothing. Then
/// it returns the processed string.
/// ### Windows Only
/// Doesn't do anything on non-windows systems because escaped output
/// (containing `\ `) is still treated as two arguments because of the
/// remaining space character.
pub fn escape_argument_spaces<S: Into<String>>(arg: S) -> String {
    #[cfg(target_os = "windows")]
    let escaped = arg.into().replace(" ", "%20");

    #[cfg(not(target_os = "windows"))]
    let escaped = arg.into();
    // Doesn't work because there's still a space character in the string
    // and it's still interpreted as a separation between two arguments.
    /*
    let escaped = arg.into().replace(" ", "\\ ");
    */

    escaped
}