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
#[macro_use] extern crate lazy_static;
extern crate pwd;
extern crate dirs;
use std::{
io,
path::{PathBuf, MAIN_SEPARATOR}
};
use pwd::Passwd;
lazy_static! {
static ref PREFIX: String = format!("~{}", MAIN_SEPARATOR);
}
pub fn expanduser<S: AsRef<str>>(s: S) -> io::Result<PathBuf> {
_expand_user(s.as_ref())
}
fn _expand_user(s: &str) -> io::Result<PathBuf> {
Ok(match s {
s if s == "~" => {
home_dir()?
},
s if s.starts_with(&*PREFIX) => {
let home = home_dir()?;
home.join(&s[2..])
},
s if s.starts_with("~") => {
let mut parts = s[1..].splitn(2, MAIN_SEPARATOR);
let user = parts.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, "malformed path"))?;
let user = Passwd::from_name(&user)
.map_err(|_| io::Error::new(io::ErrorKind::Other, "error searching for user"))?
.ok_or_else(|| io::Error::new(io::ErrorKind::Other, format!("user '{}', does not exist", &user)))?;
if let Some(ref path) = parts.next() {
PathBuf::from(user.dir).join(&path)
} else {
PathBuf::from(user.dir)
}
},
s => PathBuf::from(s)
})
}
fn home_dir() -> io::Result<PathBuf> {
dirs::home_dir().ok_or_else(|| io::Error::new(io::ErrorKind::Other, "no home directory is set"))
}
#[cfg(test)]
mod tests {
use std::env;
use super::*;
#[test]
fn test_success() {
let old_home = env::var("HOME").expect("no home dir set");
let new_home = "/home/foo";
env::set_var("HOME", new_home);
let path = expanduser("~/path/to/directory");
env::set_var("HOME", old_home);
assert_eq!(path.expect("io error"), PathBuf::from("/home/foo/path/to/directory"));
}
#[test]
fn test_only_tilde() {
let old_home = env::var("HOME").expect("no home dir set");
let new_home = "/home/foo";
env::set_var("HOME", new_home);
let pathstr = "~";
let path = expanduser(pathstr);
env::set_var("HOME", old_home);
assert_eq!(path.expect("io error"), PathBuf::from("/home/foo"));
}
#[test]
fn test_user() {
let user = env::var("USER").expect("no user set");
if user.len() < 1 {
panic!("user is empty");
}
let home = dirs::home_dir().expect("no home directory set");
let pathstr = format!("~{}/path/to/directory", &user);
let path = expanduser(&pathstr).expect("io error");
assert_eq!(path, home.join("path/to/directory"));
}
#[test]
fn test_just_tilde_user() {
let user = env::var("USER").expect("no user set");
if user.len() < 1 {
panic!("user is empty");
}
let home = dirs::home_dir().expect("no home directory set");
let pathstr = format!("~{}", &user);
let path = expanduser(&pathstr).expect("io error");
assert_eq!(path, home);
}
#[test]
fn test_fail_malformed_path() {
let pathstr = "~\ruses-invalid-path-char";
let err = expanduser(&pathstr).unwrap_err();
let kind = err.kind();
assert_eq!(kind, io::ErrorKind::Other);
}
#[test]
#[should_panic]
fn test_user_does_not_exist() {
expanduser("~user_that_should_not_exist/path/to/directory")
.expect("user does not exist");
}
}