xargo_lib/
util.rs

1use std::fs;
2use std::fs::File;
3use std::io::{Read, Write};
4use std::path::Path;
5
6use anyhow::{anyhow, Context, Result};
7use toml::Value;
8use walkdir::WalkDir;
9
10pub fn cp_r(src: &Path, dst: &Path) -> Result<()> {
11    for e in WalkDir::new(src) {
12        // This is only an error when there's some sort of intermittent IO error
13        // during iteration.
14        // see https://doc.rust-lang.org/std/fs/struct.ReadDir.html
15        let e = e.with_context(|| {
16            format!(
17                "intermittent IO error while iterating directory `{}`",
18                src.display()
19            )
20        })?;
21
22        let src_file = e.path();
23        let relative_path = src_file.strip_prefix(src).with_context(|| {
24            format!(
25                "Could not retrieve relative path of child directory or \
26                 file `{}` with regards to parent directory `{}`",
27                src_file.display(),
28                src.display()
29            )
30        })?;
31
32        let dst_file = dst.join(relative_path);
33        let metadata = e
34            .metadata()
35            .with_context(|| format!("Could not retrieve metadata of `{}`", e.path().display()))?;
36
37        if metadata.is_dir() {
38            // ensure the destination directory exists
39            fs::create_dir_all(&dst_file)
40                .with_context(|| format!("Could not create directory `{}`", dst_file.display()))?;
41        } else {
42            // else copy the file
43            fs::copy(&src_file, &dst_file).with_context(|| {
44                format!(
45                    "copying files from `{}` to `{}` failed",
46                    src_file.display(),
47                    dst_file.display()
48                )
49            })?;
50        };
51    }
52
53    Ok(())
54}
55
56pub fn mkdir(path: &Path) -> Result<()> {
57    fs::create_dir(path).with_context(|| format!("couldn't create directory {}", path.display()))
58}
59
60/// Parses `path` as TOML
61pub fn parse(path: &Path) -> Result<Value> {
62    Ok(read(path)?
63        .parse::<Value>()
64        .map_err(|e| anyhow!("{} is not valid TOML: {}", path.display(), e))?)
65}
66
67pub fn read(path: &Path) -> Result<String> {
68    let mut s = String::new();
69
70    let p = path.display();
71    File::open(path)
72        .with_context(|| format!("couldn't open {}", p))?
73        .read_to_string(&mut s)
74        .with_context(|| format!("couldn't read {}", p))?;
75
76    Ok(s)
77}
78
79/// Search for `file` in `path` and its parent directories
80pub fn search<'p>(mut path: &'p Path, file: &str) -> Option<&'p Path> {
81    loop {
82        if path.join(file).exists() {
83            return Some(path);
84        }
85
86        if let Some(p) = path.parent() {
87            path = p;
88        } else {
89            return None;
90        }
91    }
92}
93
94pub fn write(path: &Path, contents: &str) -> Result<()> {
95    let p = path.display();
96    File::create(path)
97        .with_context(|| format!("couldn't open {}", p))?
98        .write_all(contents.as_bytes())
99        .with_context(|| format!("couldn't write to {}", p))
100}