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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
extern crate pulldown_cmark;

use std::path::{Path, Component};
use std::error::Error;
use std::io;
use std::fs::{self, metadata, File};

use self::pulldown_cmark::{Parser, html, Options, OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};

/// Takes a path and returns a path containing just enough `../` to point to the root of the given path.
///
/// This is mostly interesting for a relative path to point back to the directory from where the
/// path starts.
///
/// ```ignore
/// let mut path = Path::new("some/relative/path");
///
/// println!("{}", path_to_root(&path));
/// ```
///
/// **Outputs**
///
/// ```text
/// "../../"
/// ```
///
/// **note:** it's not very fool-proof, if you find a situation where it doesn't return the correct
/// path. Consider [submitting a new issue](https://github.com/azerupi/mdBook/issues) or a
/// [pull-request](https://github.com/azerupi/mdBook/pulls) to improve it.

pub fn path_to_root(path: &Path) -> String {
    debug!("[fn]: path_to_root");
    // Remove filename and add "../" for every directory

    path.to_path_buf().parent().expect("")
        .components().fold(String::new(), |mut s, c| {
            match c {
                Component::Normal(_) => s.push_str("../"),
                _ => {
                    debug!("[*]: Other path component... {:?}", c);
                }
            }
            s
        })
}



/// This function creates a file and returns it. But before creating the file it checks every
/// directory in the path to see if it exists, and if it does not it will be created.

pub fn create_file(path: &Path) -> Result<File, Box<Error>> {
    debug!("[fn]: create_file");

    // Construct path
    if let Some(p) = path.parent() {
        debug!("Parent directory is: {:?}", p);

        try!(fs::create_dir_all(p));
    }

    debug!("[*]: Create file: {:?}", path);
    let f = match File::create(path) {
        Ok(f) => f,
        Err(e) => {
            debug!("File::create:    {}", e);
            return Err(Box::new(io::Error::new(io::ErrorKind::Other, format!("{}", e))))
        },
    };

    Ok(f)
}

/// Removes all the content of a directory but not the directory itself

pub fn remove_dir_content(dir: &Path) -> Result<(), Box<Error>> {
    for item in try!(fs::read_dir(dir)) {
        if let Ok(item) = item {
            let item = item.path();
            if item.is_dir() { try!(fs::remove_dir_all(item)); } else { try!(fs::remove_file(item)); }
        }
    }
    Ok(())
}

///
///
/// Copies all files of a directory to another one except the files with the extensions given in the
/// `ext_blacklist` array

pub fn copy_files_except_ext(from: &Path, to: &Path, recursive: bool, ext_blacklist: &[&str]) -> Result<(), Box<Error>> {
    debug!("[fn] copy_files_except_ext");
    // Check that from and to are different
    if from == to { return Ok(()) }
    debug!("[*] Loop");
    for entry in try!(fs::read_dir(from)) {
        let entry = try!(entry);
        debug!("[*] {:?}", entry.path());
        let metadata = try!(entry.metadata());

        // If the entry is a dir and the recursive option is enabled, call itself
        if metadata.is_dir() && recursive {
            if entry.path() == to.to_path_buf() { continue }
            debug!("[*] is dir");

            // check if output dir already exists
            if !to.join(entry.file_name()).exists() {
                try!(fs::create_dir(&to.join(entry.file_name())));
            }

            try!(copy_files_except_ext(
                &from.join(entry.file_name()),
                &to.join(entry.file_name()),
                true,
                ext_blacklist
            ));
        } else if metadata.is_file() {

            // Check if it is in the blacklist
            if let Some(ext) = entry.path().extension() {
                if ext_blacklist.contains(&ext.to_str().unwrap()) { continue }
                debug!("[*] creating path for file: {:?}", &to.join(entry.path().file_name().expect("a file should have a file name...")));

                output!("[*] copying file: {:?}\n    to {:?}", entry.path(), &to.join(entry.path().file_name().expect("a file should have a file name...")));
                try!(fs::copy(entry.path(), &to.join(entry.path().file_name().expect("a file should have a file name..."))));
            }
        }
    }
    Ok(())
}


///
///
/// Wrapper around the pulldown-cmark parser and renderer to render markdown

pub fn render_markdown(text: &str) -> String {
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(&text, opts);
    html::push_html(&mut s, p);
    s
}



// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------

// tests

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use super::copy_files_except_ext;
    use std::fs;

    #[test]
    fn copy_files_except_ext_test() {
        let tmp = match tempdir::TempDir::new("") {
            Ok(t) => t,
            Err(_) => panic!("Could not create a temp dir"),
        };

        // Create a couple of files
        if let Err(_) =  fs::File::create(&tmp.path().join("file.txt")) { panic!("Could not create file.txt") }
        if let Err(_) =  fs::File::create(&tmp.path().join("file.md")) { panic!("Could not create file.md") }
        if let Err(_) =  fs::File::create(&tmp.path().join("file.png")) { panic!("Could not create file.png") }
        if let Err(_) =  fs::create_dir(&tmp.path().join("sub_dir")) { panic!("Could not create sub_dir") }
        if let Err(_) =  fs::File::create(&tmp.path().join("sub_dir/file.png")) { panic!("Could not create sub_dir/file.png") }
        if let Err(_) =  fs::create_dir(&tmp.path().join("sub_dir_exists")) { panic!("Could not create sub_dir_exists") }
        if let Err(_) =  fs::File::create(&tmp.path().join("sub_dir_exists/file.txt")) { panic!("Could not create sub_dir_exists/file.txt") }

        // Create output dir
        if let Err(_) =  fs::create_dir(&tmp.path().join("output")) { panic!("Could not create output") }
        if let Err(_) =  fs::create_dir(&tmp.path().join("output/sub_dir_exists")) { panic!("Could not create output/sub_dir_exists") }

        match copy_files_except_ext(&tmp.path(), &tmp.path().join("output"), true, &["md"]) {
            Err(e) => panic!("Error while executing the function:\n{:?}", e),
            Ok(_) => {},
        }

        // Check if the correct files where created
        if !(&tmp.path().join("output/file.txt")).exists() { panic!("output/file.txt should exist") }
        if (&tmp.path().join("output/file.md")).exists() { panic!("output/file.md should not exist") }
        if !(&tmp.path().join("output/file.png")).exists() { panic!("output/file.png should exist") }
        if !(&tmp.path().join("output/sub_dir/file.png")).exists() { panic!("output/sub_dir/file.png should exist") }
        if !(&tmp.path().join("output/sub_dir_exists/file.txt")).exists() { panic!("output/sub_dir/file.png should exist") }

    }
}