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
use std::fs;
use std::path::{Path, PathBuf};
use std::io::{Error, ErrorKind, Result};

/// Copy a file along with those files that the file refers to.
pub fn with_references(source: &Path, destination: &Path) -> Result<()> {
    use std::fs::File;
    use std::io::{BufRead, BufReader, BufWriter, Write};

    macro_rules! some(
        ($option:expr) => (match $option {
            Some(some) => some,
            None => return Err(Error::new(ErrorKind::Other, "something went wrong")),
        });
    );

    macro_rules! next(
        ($writer:expr, $line:expr) => ({
            try!($writer.write($line.as_bytes()));
            try!($writer.write(b"\n"));
            continue;
        });
    );

    let from = some!(source.parent());
    let into = some!(destination.parent());

    let mut source = try!(File::open(source));
    let reader = BufReader::new(&mut source);

    let mut destination = try!(File::create(destination));
    let mut writer = BufWriter::new(&mut destination);

    for line in reader.lines() {
        let line = try!(line);

        let i = match line.find('"') {
            Some(i) => i,
            _ => next!(writer, line),
        };
        let j = match line.rfind('"') {
            Some(j) => j,
            _ => next!(writer, line),
        };

        let (prefix, middle, suffix) = (&line[..i+1], &line[i+1..j], &line[j..]);

        let path = PathBuf::from(middle);
        let name = match path.file_name() {
            Some(name) => PathBuf::from(name),
            _ => next!(writer, line),
        };

        let (source, destination) = if path.is_relative() {
            (from.join(&name), into.join(&name))
        } else {
            (path, into.join(&name))
        };

        let metadata = match fs::metadata(&source) {
            Ok(metadata) => metadata,
            _ => next!(writer, line),
        };
        if metadata.is_dir() {
            next!(writer, line);
        }

        try!(fs::copy(&source, &destination));
        try!(writer.write(prefix.as_bytes()));
        try!(writer.write(some!(destination.to_str()).as_bytes()));
        try!(writer.write(suffix.as_bytes()));
        try!(writer.write(b"\n"));
    }

    Ok(())
}