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
use glob;
use retry::retry;
use std::env;
use std::error;
use std::ffi::OsStr;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use errors::*;
#[macro_export]
macro_rules! err {
($( $e:expr ),+) => ($crate::err(&format!($( $e ),+)))
}
pub fn err(msg: &str) -> Error {
msg.into()
}
pub trait ToStrOrErr {
fn to_str_or_err(&self) -> Result<&str>;
}
impl ToStrOrErr for OsStr {
fn to_str_or_err(&self) -> Result<&str> {
self.to_str().ok_or_else(|| {
err!("the string {:?} contains non-Unicode characters", self)
})
}
}
impl ToStrOrErr for Path {
fn to_str_or_err(&self) -> Result<&str> {
self.to_str().ok_or_else(|| {
err!("the path {} contains non-Unicode characters",
self.display())
})
}
}
pub trait ConductorPathExt: ToStrOrErr {
fn glob(&self, pattern: &str) -> Result<glob::Paths>;
fn with_guaranteed_parent(&self) -> Result<PathBuf>;
fn to_absolute(&self) -> Result<PathBuf>;
}
impl ConductorPathExt for Path {
fn glob(&self, pattern: &str) -> Result<glob::Paths> {
let opts = glob::MatchOptions {
case_sensitive: true,
require_literal_separator: true,
require_literal_leading_dot: true,
};
let pat = format!("{}/{}", try!(self.to_str_or_err()), pattern);
Ok(try!(glob::glob_with(&pat, &opts)))
}
fn with_guaranteed_parent(&self) -> Result<PathBuf> {
let parent = try!(self.parent()
.ok_or_else(|| err!("can't find parent path of {}", self.display())));
let wrap_err = |err: &error::Error| -> Error {
err!("error creating parent directories for {}: {}",
parent.display(),
err)
};
let retry_fn = || {
fs::create_dir_all(&parent)
};
let retry_result = retry(5, 50, retry_fn, |result| {
match *result {
Err(ref err) if err.kind() == io::ErrorKind::AlreadyExists => false,
_ => true,
}
});
try!(try!(retry_result.map_err(|e| wrap_err(&e))).map_err(|e| wrap_err(&e)));
Ok(self.to_owned())
}
fn to_absolute(&self) -> Result<PathBuf> {
let path = try!(env::current_dir()).join(self);
assert!(path.is_absolute());
Ok(path)
}
}
#[test]
fn path_glob_uses_path_as_base() {
let base = Path::new("examples/hello/pods/overrides");
let paths: Vec<_> = base.glob("test/*.env")
.unwrap()
.map(|p| p.unwrap().strip_prefix(base).unwrap().to_owned())
.collect();
assert_eq!(paths, vec![Path::new("test/common.env")]);
}