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
use crate::{
common::{util, AbsolutePath, FormattedItem, FormattedItems},
verbose_println,
};
use derive_more::From;
use failure::Fail;
use std::{
fs,
io::{self, Write},
path::Path,
};
enum YN {
Yes,
No,
}
use self::YN::*;
fn read_yes_or_no(prompt: &str) -> io::Result<YN> {
let mut buf = String::new();
loop {
print!("{} (y/n) ", prompt);
io::stdout().flush()?;
io::stdin().read_line(&mut buf)?;
buf = buf.trim().to_lowercase();
if buf.is_empty() {
continue;
}
if buf.starts_with("yes") || "yes".starts_with(&buf) {
return Ok(Yes);
} else if buf.starts_with("no") || "no".starts_with(&buf) {
return Ok(No);
} else {
buf.clear();
continue;
}
}
}
#[cfg(unix)]
fn symlink(source: impl AsRef<Path>, dest: impl AsRef<Path>) -> io::Result<()> {
std::os::unix::fs::symlink(source, dest)
}
fn link_item(item: &FormattedItem, dry_run: bool) -> Result<(), Error> {
let (source, dest) = (item.source(), item.dest());
let link = |item: &FormattedItem| -> Result<(), Error> {
verbose_println!("Linking {}", item);
if dry_run {
return Ok(());
}
fs::create_dir_all(dest.parent().unwrap_or(dest))?;
symlink(source, dest)?;
Ok(())
};
if !dest.exists() {
link(item)?
} else {
match fs::read_link(dest) {
Ok(ref target) if target.as_path() == source.as_path() => {
verbose_println!("Skipping identical {}", dest)
},
_ => {
let prompt = format!("Overwrite {}?", dest);
match read_yes_or_no(&prompt)? {
No => println!("Skipping {}", dest),
Yes => {
match util::file_type(dest)? {
util::FileType::File | util::FileType::Symlink => {
fs::remove_file(dest)?
},
util::FileType::Directory => {
return Err(DirectoryOverwrite(dest.clone()))
},
};
link(item)?;
},
}
},
}
}
Ok(())
}
pub fn link_items(items: FormattedItems, dry_run: bool) -> Result<(), Error> {
for item in &items {
link_item(item, dry_run)?;
}
Ok(())
}
#[derive(Debug, From, Fail)]
pub enum Error {
#[fail(display = "error creating symlinks ({})", _0)]
IoError(#[fail(cause)] io::Error),
#[fail(
display = "won't delete directory {}. Please remove it manually if you want.",
_0
)]
DirectoryOverwrite(AbsolutePath),
}
use self::Error::*;