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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! Utility functions.

pub mod config;
pub mod errors;
pub mod git;
pub mod graph;
pub mod lock;
pub mod read2;
pub mod shell;

pub use crate::util::read2::read2;

use crate::util::errors::{Error, Res};
use failure::{bail, format_err, ResultExt};
use itertools::Itertools;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{
    fs,
    path::{Component, Path, PathBuf},
    process::Output,
    str::FromStr,
};
use walkdir::{DirEntry, WalkDir};

/// Turns an SHA2 hash into a nice hexified string.
pub fn hexify_hash(hash: &[u8]) -> String {
    let mut s = String::new();
    for byte in hash {
        let p = format!("{:02x}", byte);
        s.push_str(&p);
    }
    s
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SubPath(pub PathBuf);

impl SubPath {
    pub fn is_subpath(p: &Path) -> bool {
        p.is_relative() && p.components().all(|x| x != Component::ParentDir)
    }

    pub fn from_path(p: &Path) -> Res<Self> {
        if SubPath::is_subpath(&p) {
            Ok(SubPath(p.to_path_buf()))
        } else {
            bail!("p {} isn't a strict subdirectory", p.display())
        }
    }
}

impl FromStr for SubPath {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let path = PathBuf::from(s);
        SubPath::from_path(&path)
    }
}

impl Serialize for SubPath {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(self.0.to_string_lossy().as_ref())
    }
}

impl<'de> Deserialize<'de> for SubPath {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let s = String::deserialize(deserializer)?;
        FromStr::from_str(&s).map_err(de::Error::custom)
    }
}

pub fn copy_dir_iter(walker: impl Iterator<Item = DirEntry>, from: &Path, to: &Path) -> Res<()> {
    for entry in walker {
        let to_p = to.join(entry.path().strip_prefix(from).unwrap());
        // Make sure that the file exists before we try copying
        fs::create_dir_all(to_p.parent().unwrap())?;
        fs::File::create(&to_p).context(format_err!("couldn't create file {}", to_p.display()))?;
        let _ = fs::copy(entry.path(), &to_p).with_context(|e| {
            format_err!(
                "couldn't copy {} to {}:\n{}",
                entry.path().display(),
                to_p.display(),
                e
            )
        })?;
    }

    Ok(())
}

pub fn copy_dir(from: &Path, to: &Path, gitless: bool) -> Res<()> {
    let walker = WalkDir::new(from)
        .follow_links(true)
        .into_iter()
        .filter_entry(|x| x.path() != to && (!gitless || x.file_name() != ".git"))
        .filter_map(|x| {
            x.ok()
                .and_then(|x| if valid_file(&x) { Some(x) } else { None })
        });

    copy_dir_iter(walker, from, to)
}

pub fn clear_dir(dir: &Path) -> Res<()> {
    if dir.exists() {
        fs::remove_dir_all(dir)?;
    }
    fs::create_dir_all(dir)?;
    Ok(())
}

pub fn valid_file(entry: &DirEntry) -> bool {
    entry.file_type().is_file()
}

pub fn generate_ipkg(name: &str, src_dir: &str, opts: &str, mods: &str) -> String {
    format!(
        r#"package {}
sourcedir = {}
opts = "{}"
modules = {}
    "#,
        name, src_dir, opts, mods
    )
}

pub fn fmt_multiple(c: &shell::OutputGroup) -> String {
    c.0.iter().map(|x| fmt_output(&x)).join("\n")
}

pub fn fmt_output(c: &Output) -> String {
    let mut res = String::new();
    if !c.stderr.is_empty() {
        if !c.stdout.is_empty() {
            res.push_str("--- stdout\n");
        }
        res.push_str(format!("{}\n", String::from_utf8_lossy(&c.stderr)).as_ref());
        res.push_str(format!("--- stderr\n{}\n", String::from_utf8_lossy(&c.stderr)).as_ref());
    }
    if !c.stdout.is_empty() {
        if !c.stderr.is_empty() {
            res.push_str("--- stdout\n");
        }
        res.push_str(format!("{}\n", String::from_utf8_lossy(&c.stdout)).as_ref());
    }
    // Remove the ending newline if it exists
    res.pop();
    res
}