<commit_before>extern crate chrono;
extern crate walkdir;

use self::chrono::{Duration, UTC};
use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::borrow::Borrow;
use std::collections::{BTreeSet, VecDeque};
use std::fs::{self, DirEntry};
use std::io::Error as IoError;
use std::os::unix::fs::MetadataExt;
use std::path::{MAIN_SEPARATOR, Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => ({
        let mut path = PathBuf::new();
        $(path.push($x);)+
        path
    });
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}

trait DirEntryExt {
    fn file_name_string(&self) -> Option<String> {
        unimplemented!();
    }

    fn is_hidden(&self) -> bool;
}
impl DirEntryExt for DirEntry {
    fn file_name_string(&self) -> Option<String> {
        self
            .file_name()
            .into_string()
            .ok()
    }

    fn is_hidden(&self) -> bool {
        self
            .file_name()
            .to_str()
            .map_or(false, is_hidden_name)
    }
}
impl DirEntryExt for WalkDirEntry {
    fn is_hidden(&self) -> bool {
        self
            .file_name()
            .to_str()
            .map_or(false, is_hidden_name)
    }
}

pub fn supply_current_dir_prefix<S: AsRef<str>>(path_name: S) -> String {
    let path_name = path_name.as_ref();

    if path_name.starts_with(".") || path_name.starts_with("/") {
        path_name.to_string()
    } else {
        format!(".{}{}", MAIN_SEPARATOR, path_name)
    }
}

pub fn la<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .filter_map(|e| e.file_name_string())
        .collect::<Vec<String>>();
    Ok(dirs)
}

#[cfg(unix)]
pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(p: P, moratorium: D) -> bool {
    let threshold = UTC::now() - *moratorium.borrow();

    let accessed_time = match p.as_ref().metadata() {
        Ok(m)  => m.atime(),
        Err(_) => unreachable!("Wrong to use this function!!"),
    };

    accessed_time > threshold.timestamp()
}
#[cfg(windows)]
pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(_p: P, _moratorium: D) -> bool {
    false
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !e.is_hidden())
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(|p| p.to_string()))
        .collect::<BTreeSet<String>>()
}

pub fn potentially_empty_dirs<P: AsRef<Path>>(root: P, ignored_entries: Vec<PathBuf>) -> BTreeSet<PathBuf> {
    fn potentially_empty_dirs(mut result: BTreeSet<PathBuf>, mut target_dirs: VecDeque<PathBuf>, ignored_entries: Vec<PathBuf>) -> BTreeSet<PathBuf> {
        match target_dirs.pop_front() {
            None => result,
            Some(mut target_dir) => {
                let read_result = fs::read_dir(&target_dir);
                let ignore      = read_result.is_err();

                let entries = match read_result {
                    Ok(rd) => rd
                        .filter_map(Result::ok)
                        .collect::<Vec<DirEntry>>(),
                    Err(_) => Vec::new(),
                };
                let include_file_or_hidden_dir = entries
                    .iter()
                    .filter(|e| ignored_entries.iter().all(|ie| *ie != e.path()))
                    .any(|e| e.file_type().ok().map_or(true, |t| t.is_file()) || e.is_hidden());

                if ignore || include_file_or_hidden_dir {
                    loop {
                        if !(result.remove(&target_dir) && target_dir.pop()) {
                            break;
                        }
                    }
                }

                let dirs = entries
                    .iter()
                    .filter(|e| e.file_type().ok().map_or(false, |t| t.is_dir()) && !e.is_hidden())
                    .map(|e| e.path())
                    .collect::<BTreeSet<PathBuf>>();
                for dir in dirs.clone().into_iter() {
                    result.insert(dir);
                }

                let mut dirs = dirs.into_iter().collect::<VecDeque<PathBuf>>();
                target_dirs.append(&mut dirs);

                potentially_empty_dirs(result, target_dirs, ignored_entries)
            },
        }
    }

    let mut result     : BTreeSet<PathBuf> = BTreeSet::new();
    let mut target_dirs: VecDeque<PathBuf> = VecDeque::new();

    let root = root.as_ref().to_path_buf();
    result.insert(root.clone());
    target_dirs.push_back(root);

    potentially_empty_dirs(result, target_dirs, ignored_entries)
}

#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::BTreeSet;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    struct Helper {
        d1: String,
        d2: String,
        d3: String,
        d4: String,
        d5: String,
        d6: String,
        f1: String,
        f2: String,
        f3: String,
        f4: String,
    }
    impl Helper {
        fn new(suffix: &str) -> Self {
            Helper {
                d1: format!("directory1_{}" , suffix),
                d2: format!("directory2_{}" , suffix),
                d3: format!("directory3_{}" , suffix),
                d4: format!("directory4_{}" , suffix),
                d5: format!("directory5_{}" , suffix),
                d6: format!(".directory6_{}", suffix),
                f1: format!("file1_{}"      , suffix),
                f2: format!("file2_{}"      , suffix),
                f3: format!("file3_{}"      , suffix),
                f4: format!(".file4_{}"     , suffix),
            }
        }

        fn path_to_d1(&self) -> PathBuf {
            PathBuf::new().join(&self.d1)
        }
        fn path_to_d2(&self) -> PathBuf {
            self.path_to_d1().join(&self.d2)
        }
        fn path_to_d3(&self) -> PathBuf {
            self.path_to_d2().join(&self.d3)
        }
        fn path_to_d4(&self) -> PathBuf {
            self.path_to_d3().join(&self.d4)
        }
        fn path_to_d5(&self) -> PathBuf {
            self.path_to_d1().join(&self.d5)
        }
        fn path_to_d6(&self) -> PathBuf {
            self.path_to_d1().join(&self.d6)
        }

        fn path_to_f1(&self) -> PathBuf {
            self.path_to_d1().join(&self.f1)
        }
        fn path_to_f2(&self) -> PathBuf {
            self.path_to_d2().join(&self.f2)
        }
        fn path_to_f3(&self) -> PathBuf {
            self.path_to_d3().join(&self.f3)
        }
        fn path_to_f4(&self) -> PathBuf {
            self.path_to_d5().join(&self.f4)
        }

        fn remove_dirs_and_files(&self) {
            fs::remove_dir_all(self.path_to_d1()).ok();
        }

        fn create_dirs_and_files(&self) {
            self.remove_dirs_and_files();

            fs::create_dir_all(self.path_to_d4()).ok();
            fs::create_dir_all(self.path_to_d5()).ok();
            fs::create_dir_all(self.path_to_d6()).ok();

            let mut f = File::create(self.path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f2()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f3()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f4()).ok();
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn supply_current_dir_prefix_should_return_added() {
        let path_names = [
            ("./a"  , "a"  ),
            ("./a/" , "a/" ),
            ("./a/b", "a/b"),
        ];
        for &(correct, input) in &path_names {
            assert_eq!(correct.to_string(), supply_current_dir_prefix(input));
        }
    }
    #[test]
    fn supply_current_dir_prefix_should_return_as_it_is() {
        let path_names = [
            "/",
            "/a",
            ".",
            "./",
            "./a",
            "././a",
        ];
        for path_name in &path_names {
            assert_eq!(path_name.to_string(), supply_current_dir_prefix(path_name))
        }
    }

    #[test]
    fn la_should_return_ok() {
        fn to_btree_set(vec: Vec<String>) -> BTreeSet<String> {
            vec.into_iter().collect::<BTreeSet<String>>()
        }

        let helper = Helper::new("la_Ok");
        helper.create_dirs_and_files();

        let data_set = vec![
            (vec![helper.d2.clone(), helper.d5.clone(), helper.d6.clone(), helper.f1.clone()], helper.path_to_d1()),
            (vec![helper.d3.clone(), helper.f2.clone()]                                      , helper.path_to_d2()),
            (vec![helper.d4.clone(), helper.f3.clone()]                                      , helper.path_to_d3()),
            (Vec::new()                                                                      , helper.path_to_d4()),
            (vec![helper.f4.clone()]                                                         , helper.path_to_d5()),
            (Vec::new()                                                                      , helper.path_to_d6()),
        ];

        for (correct, target_dir) in data_set.into_iter() {
            assert_eq!(to_btree_set(correct), to_btree_set(la(target_dir).unwrap()));
        }

        helper.remove_dirs_and_files();
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
        let helper = Helper::new("walk_dir_BTreeSet");
        helper.create_dirs_and_files();

        let mut correct = BTreeSet::new();
        let files = [helper.path_to_f1(), helper.path_to_f2(), helper.path_to_f3()];
        for file in &files {
            correct.insert(file.to_str().unwrap().to_string());
        }

        assert_eq!(correct, walk_dir(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn potentially_empty_dirs_should_return_btree_set() {
        let helper = Helper::new("potentially_empty_dirs_BTreeSet");
        helper.create_dirs_and_files();

        let root = helper.path_to_d1();

        let mut correct = BTreeSet::new();
        correct.insert(helper.path_to_d4());
        let mut ignored_files = Vec::new();
        let data_set = vec![
            (helper.path_to_d3(), helper.path_to_f3()),
            (helper.path_to_d2(), helper.path_to_f2()),
            (helper.path_to_d5(), helper.path_to_f4()),
        ];
        for (part_of_correct, ignored_file) in data_set.into_iter() {
            correct.insert(part_of_correct);
            ignored_files.push(ignored_file);
            assert_eq!(correct, potentially_empty_dirs(&root, ignored_files.clone()));
        }

        correct.clear();
        correct.insert(helper.path_to_d4());
        let data_set = vec![
            (helper.path_to_d4(), None),
            (helper.path_to_d3(), Some(helper.path_to_f3())),
            (helper.path_to_d2(), Some(helper.path_to_f2())),
            (helper.path_to_d5(), Some(helper.path_to_f4())),
        ];
        for (part_of_correct, removed_file) in data_set.into_iter() {
            correct.insert(part_of_correct);
            if let Some(file) = removed_file {
                fs::remove_file(file).ok();
            }
            assert_eq!(correct, potentially_empty_dirs(&root, Vec::new()));
        }

        helper.remove_dirs_and_files();
    }
}
<commit_msg>[Fix] Fix judge if "current_dir_prefix" exists<commit_after>extern crate chrono;
extern crate walkdir;

use self::chrono::{Duration, UTC};
use self::walkdir::{DirEntry as WalkDirEntry, WalkDir, WalkDirIterator};

use std::borrow::Borrow;
use std::collections::{BTreeSet, VecDeque};
use std::fs::{self, DirEntry};
use std::io::Error as IoError;
use std::os::unix::fs::MetadataExt;
use std::path::{Component, MAIN_SEPARATOR, Path, PathBuf};
use std::result::Result;

macro_rules! path_buf {
    ($($x: expr),+) => ({
        let mut path = PathBuf::new();
        $(path.push($x);)+
        path
    });
}

fn is_hidden_name(file_name: &str) -> bool {
    file_name.starts_with(".") && file_name.len() > 1 && file_name != ".."
}

trait DirEntryExt {
    fn file_name_string(&self) -> Option<String> {
        unimplemented!();
    }

    fn is_hidden(&self) -> bool;
}
impl DirEntryExt for DirEntry {
    fn file_name_string(&self) -> Option<String> {
        self
            .file_name()
            .into_string()
            .ok()
    }

    fn is_hidden(&self) -> bool {
        self
            .file_name()
            .to_str()
            .map_or(false, is_hidden_name)
    }
}
impl DirEntryExt for WalkDirEntry {
    fn is_hidden(&self) -> bool {
        self
            .file_name()
            .to_str()
            .map_or(false, is_hidden_name)
    }
}

pub fn supply_current_dir_prefix<S: AsRef<str>>(path_name: S) -> String {
    let path_name = path_name.as_ref();

    match Path::new(path_name).components().next() {
        Some(entry) => match entry {
            Component::CurDir | Component::RootDir | Component::Prefix(_) => path_name.to_string(),
            _                                                             => format!(".{}{}", MAIN_SEPARATOR, path_name),
        },
        None => unreachable!("Wrong to use this function!!"),
    }
}

pub fn la<P: AsRef<Path>>(path: P) -> Result<Vec<String>, IoError> {
    let dirs = try!(fs::read_dir(path));
    let dirs = dirs
        .filter_map(Result::ok)
        .filter_map(|e| e.file_name_string())
        .collect::<Vec<String>>();
    Ok(dirs)
}

#[cfg(unix)]
pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(p: P, moratorium: D) -> bool {
    let threshold = UTC::now() - *moratorium.borrow();

    let accessed_time = match p.as_ref().metadata() {
        Ok(m)  => m.atime(),
        Err(_) => unreachable!("Wrong to use this function!!"),
    };

    accessed_time > threshold.timestamp()
}
#[cfg(windows)]
pub fn is_recently_accessed<P: AsRef<Path>, D: Borrow<Duration>>(_p: P, _moratorium: D) -> bool {
    false
}

pub fn walk_dir<P: AsRef<Path>>(root: P) -> BTreeSet<String> {
    let walker = WalkDir::new(root)
        .into_iter()
        .filter_entry(|e| !e.is_hidden())
        .filter_map(Result::ok)
        .filter(|e| !e.file_type().is_dir())
        .collect::<Vec<WalkDirEntry>>();

    walker
        .into_iter()
        .filter_map(|e| e.path().to_str().map(|p| p.to_string()))
        .collect::<BTreeSet<String>>()
}

pub fn potentially_empty_dirs<P: AsRef<Path>>(root: P, ignored_entries: Vec<PathBuf>) -> BTreeSet<PathBuf> {
    fn potentially_empty_dirs(mut result: BTreeSet<PathBuf>, mut target_dirs: VecDeque<PathBuf>, ignored_entries: Vec<PathBuf>) -> BTreeSet<PathBuf> {
        match target_dirs.pop_front() {
            None => result,
            Some(mut target_dir) => {
                let read_result = fs::read_dir(&target_dir);
                let ignore      = read_result.is_err();

                let entries = match read_result {
                    Ok(rd) => rd
                        .filter_map(Result::ok)
                        .collect::<Vec<DirEntry>>(),
                    Err(_) => Vec::new(),
                };
                let include_file_or_hidden_dir = entries
                    .iter()
                    .filter(|e| ignored_entries.iter().all(|ie| *ie != e.path()))
                    .any(|e| e.file_type().ok().map_or(true, |t| t.is_file()) || e.is_hidden());

                if ignore || include_file_or_hidden_dir {
                    loop {
                        if !(result.remove(&target_dir) && target_dir.pop()) {
                            break;
                        }
                    }
                }

                let dirs = entries
                    .iter()
                    .filter(|e| e.file_type().ok().map_or(false, |t| t.is_dir()) && !e.is_hidden())
                    .map(|e| e.path())
                    .collect::<BTreeSet<PathBuf>>();
                for dir in dirs.clone().into_iter() {
                    result.insert(dir);
                }

                let mut dirs = dirs.into_iter().collect::<VecDeque<PathBuf>>();
                target_dirs.append(&mut dirs);

                potentially_empty_dirs(result, target_dirs, ignored_entries)
            },
        }
    }

    let mut result     : BTreeSet<PathBuf> = BTreeSet::new();
    let mut target_dirs: VecDeque<PathBuf> = VecDeque::new();

    let root = root.as_ref().to_path_buf();
    result.insert(root.clone());
    target_dirs.push_back(root);

    potentially_empty_dirs(result, target_dirs, ignored_entries)
}

#[test]
fn is_hidden_name_should_return_true() {
    let file_names = [
        ".a",
        ".ab",
        ".abc",
        "..a",
        "...",
    ];
    for file_name in &file_names {
        assert!(is_hidden_name(file_name));
    }
}
#[test]
fn is_hidden_name_should_return_false() {
    let file_names = [
        "",

        ".",
        "..",
        "a",
        "ab",
        "abc",
    ];
    for file_name in &file_names {
        assert!(!is_hidden_name(file_name));
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use std::collections::BTreeSet;
    use std::fs::{self, File};
    use std::io::Write;
    use std::path::{Path, PathBuf};

    struct Helper {
        d1: String,
        d2: String,
        d3: String,
        d4: String,
        d5: String,
        d6: String,
        f1: String,
        f2: String,
        f3: String,
        f4: String,
    }
    impl Helper {
        fn new(suffix: &str) -> Self {
            Helper {
                d1: format!("directory1_{}" , suffix),
                d2: format!("directory2_{}" , suffix),
                d3: format!("directory3_{}" , suffix),
                d4: format!("directory4_{}" , suffix),
                d5: format!("directory5_{}" , suffix),
                d6: format!(".directory6_{}", suffix),
                f1: format!("file1_{}"      , suffix),
                f2: format!("file2_{}"      , suffix),
                f3: format!("file3_{}"      , suffix),
                f4: format!(".file4_{}"     , suffix),
            }
        }

        fn path_to_d1(&self) -> PathBuf {
            PathBuf::new().join(&self.d1)
        }
        fn path_to_d2(&self) -> PathBuf {
            self.path_to_d1().join(&self.d2)
        }
        fn path_to_d3(&self) -> PathBuf {
            self.path_to_d2().join(&self.d3)
        }
        fn path_to_d4(&self) -> PathBuf {
            self.path_to_d3().join(&self.d4)
        }
        fn path_to_d5(&self) -> PathBuf {
            self.path_to_d1().join(&self.d5)
        }
        fn path_to_d6(&self) -> PathBuf {
            self.path_to_d1().join(&self.d6)
        }

        fn path_to_f1(&self) -> PathBuf {
            self.path_to_d1().join(&self.f1)
        }
        fn path_to_f2(&self) -> PathBuf {
            self.path_to_d2().join(&self.f2)
        }
        fn path_to_f3(&self) -> PathBuf {
            self.path_to_d3().join(&self.f3)
        }
        fn path_to_f4(&self) -> PathBuf {
            self.path_to_d5().join(&self.f4)
        }

        fn remove_dirs_and_files(&self) {
            fs::remove_dir_all(self.path_to_d1()).ok();
        }

        fn create_dirs_and_files(&self) {
            self.remove_dirs_and_files();

            fs::create_dir_all(self.path_to_d4()).ok();
            fs::create_dir_all(self.path_to_d5()).ok();
            fs::create_dir_all(self.path_to_d6()).ok();

            let mut f = File::create(self.path_to_f1()).unwrap();
            f.write("\n".as_ref()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f2()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f3()).ok();
            fs::copy(self.path_to_f1(), self.path_to_f4()).ok();
        }
    }

    #[test]
    fn path_buf_macro_should_create_path_buf() {
        let paths = [
            (Path::new("path").to_path_buf()          , path_buf!["path"              ]),
            (Path::new("path").join("to")             , path_buf!["path", "to"        ]),
            (Path::new("path").join("to").join("file"), path_buf!["path", "to", "file"]),
        ];
        for &(ref correct, ref calculated) in &paths {
            assert_eq!(correct, calculated);
        }
    }

    #[test]
    fn supply_current_dir_prefix_should_return_added() {
        let path_names = [
            ("./a"  , "a"  ),
            ("./a/" , "a/" ),
            ("./a/b", "a/b"),
        ];
        for &(correct, input) in &path_names {
            assert_eq!(correct.to_string(), supply_current_dir_prefix(input));
        }
    }
    #[test]
    fn supply_current_dir_prefix_should_return_as_it_is() {
        let path_names = [
            "/",
            "/a",
            ".",
            "./",
            "./a",
            "././a",
        ];
        for path_name in &path_names {
            assert_eq!(path_name.to_string(), supply_current_dir_prefix(path_name))
        }
    }
    #[test]
    #[should_panic(expect = "entered unreachable code")]
    fn supply_current_dir_prefix_should_panic() {
        supply_current_dir_prefix("");
    }

    #[test]
    fn la_should_return_ok() {
        fn to_btree_set(vec: Vec<String>) -> BTreeSet<String> {
            vec.into_iter().collect::<BTreeSet<String>>()
        }

        let helper = Helper::new("la_Ok");
        helper.create_dirs_and_files();

        let data_set = vec![
            (vec![helper.d2.clone(), helper.d5.clone(), helper.d6.clone(), helper.f1.clone()], helper.path_to_d1()),
            (vec![helper.d3.clone(), helper.f2.clone()]                                      , helper.path_to_d2()),
            (vec![helper.d4.clone(), helper.f3.clone()]                                      , helper.path_to_d3()),
            (Vec::new()                                                                      , helper.path_to_d4()),
            (vec![helper.f4.clone()]                                                         , helper.path_to_d5()),
            (Vec::new()                                                                      , helper.path_to_d6()),
        ];

        for (correct, target_dir) in data_set.into_iter() {
            assert_eq!(to_btree_set(correct), to_btree_set(la(target_dir).unwrap()));
        }

        helper.remove_dirs_and_files();
    }

    #[test]
    fn walk_dir_should_return_b_tree_set() {
        let helper = Helper::new("walk_dir_BTreeSet");
        helper.create_dirs_and_files();

        let mut correct = BTreeSet::new();
        let files = [helper.path_to_f1(), helper.path_to_f2(), helper.path_to_f3()];
        for file in &files {
            correct.insert(file.to_str().unwrap().to_string());
        }

        assert_eq!(correct, walk_dir(helper.path_to_d1()));

        helper.remove_dirs_and_files();
    }

    #[test]
    fn potentially_empty_dirs_should_return_btree_set() {
        let helper = Helper::new("potentially_empty_dirs_BTreeSet");
        helper.create_dirs_and_files();

        let root = helper.path_to_d1();

        let mut correct = BTreeSet::new();
        correct.insert(helper.path_to_d4());
        let mut ignored_files = Vec::new();
        let data_set = vec![
            (helper.path_to_d3(), helper.path_to_f3()),
            (helper.path_to_d2(), helper.path_to_f2()),
            (helper.path_to_d5(), helper.path_to_f4()),
        ];
        for (part_of_correct, ignored_file) in data_set.into_iter() {
            correct.insert(part_of_correct);
            ignored_files.push(ignored_file);
            assert_eq!(correct, potentially_empty_dirs(&root, ignored_files.clone()));
        }

        correct.clear();
        correct.insert(helper.path_to_d4());
        let data_set = vec![
            (helper.path_to_d4(), None),
            (helper.path_to_d3(), Some(helper.path_to_f3())),
            (helper.path_to_d2(), Some(helper.path_to_f2())),
            (helper.path_to_d5(), Some(helper.path_to_f4())),
        ];
        for (part_of_correct, removed_file) in data_set.into_iter() {
            correct.insert(part_of_correct);
            if let Some(file) = removed_file {
                fs::remove_file(file).ok();
            }
            assert_eq!(correct, potentially_empty_dirs(&root, Vec::new()));
        }

        helper.remove_dirs_and_files();
    }
}
<|endoftext|>


<commit_before>use rustc_hash::FxHashSet;

use syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange, TextSize,
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
    Region,
    Consts,
    Statics,
    Array,
    WhereClause,
    ReturnType,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

// Feature: Folding
//
// Defines folding regions for curly braced blocks, runs of consecutive import
// statements, and `region` / `endregion` comment markers.
pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();
    let mut visited_consts = FxHashSet::default();
    let mut visited_statics = FxHashSet::default();
    // regions can be nested, here is a LIFO buffer
    let mut regions_starts: Vec<TextSize> = vec![];

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if !visited_comments.contains(&comment) {
                        // regions are not real comments
                        if comment.text().trim().starts_with("// region:") {
                            regions_starts.push(comment.syntax().text_range().start());
                        } else if comment.text().trim().starts_with("// endregion") {
                            if let Some(region) = regions_starts.pop() {
                                res.push(Fold {
                                    range: TextRange::new(
                                        region,
                                        comment.syntax().text_range().end(),
                                    ),
                                    kind: FoldKind::Region,
                                })
                            }
                        } else {
                            if let Some(range) =
                                contiguous_range_for_comment(comment, &mut visited_comments)
                            {
                                res.push(Fold { range, kind: FoldKind::Comment })
                            }
                        }
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if let Some(module) = ast::Module::cast(node.clone()) {
                    if !has_visibility(&node)
                        && !visited_mods.contains(&node)
                        && module.item_list().is_none()
                    {
                        if let Some(range) = contiguous_range_for_group_unless(
                            &node,
                            has_visibility,
                            &mut visited_mods,
                        ) {
                            res.push(Fold { range, kind: FoldKind::Mods })
                        }
                    }
                }

                // Fold groups of consts
                if node.kind() == CONST && !visited_consts.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_consts) {
                        res.push(Fold { range, kind: FoldKind::Consts })
                    }
                }
                // Fold groups of consts
                if node.kind() == STATIC && !visited_statics.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_statics) {
                        res.push(Fold { range, kind: FoldKind::Statics })
                    }
                }

                // Fold where clause
                if node.kind() == WHERE_CLAUSE {
                    if let Some(range) = fold_range_for_where_clause(&node) {
                        res.push(Fold { range, kind: FoldKind::WhereClause })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
        ARRAY_EXPR => Some(FoldKind::Array),
        RET_TYPE => Some(FoldKind::ReturnType),
        ASSOC_ITEM_LIST
        | RECORD_FIELD_LIST
        | RECORD_PAT_FIELD_LIST
        | RECORD_EXPR_FIELD_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        // regions are not real comments
                        if c.text().trim().starts_with("// region:")
                            || c.text().trim().starts_with("// endregion")
                        {
                            break;
                        } else {
                            visited.insert(c.clone());
                            last = c;
                            continue;
                        }
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn fold_range_for_where_clause(node: &SyntaxNode) -> Option<TextRange> {
    let first_where_pred = node.first_child();
    let last_where_pred = node.last_child();

    if first_where_pred != last_where_pred {
        let mut it = node.descendants_with_tokens();
        if let (Some(_where_clause), Some(where_kw), Some(last_comma)) =
            (it.next(), it.next(), it.last())
        {
            let start = where_kw.text_range().end();
            let end = last_comma.text_range().end();
            return Some(TextRange::new(start, end));
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let folds = folding_ranges(&parse.tree());
        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
                FoldKind::Region => "region",
                FoldKind::Consts => "consts",
                FoldKind::Statics => "statics",
                FoldKind::Array => "array",
                FoldKind::WhereClause => "whereclause",
                FoldKind::ReturnType => "returntype",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;
"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

mod inline0 {}
mod inline1 {}

mod inline2 <fold block>{

}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;</fold>
"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;
// Some random comment
"#,
        );
    }

    #[test]
    fn test_folds_structs() {
        check(
            r#"
struct Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_traits() {
        check(
            r#"
trait Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
"#,
        )
    }

    #[test]
    fn fold_record_literals() {
        check(
            r#"
const _: S = S <fold block>{

}</fold>;
"#,
        )
    }

    #[test]
    fn fold_multiline_params() {
        check(
            r#"
fn foo<fold arglist>(
    x: i32,
    y: String,
)</fold> {}
"#,
        )
    }

    #[test]
    fn fold_multiline_array() {
        check(
            r#"
const FOO: [usize; 4] = <fold array>[
    1,
    2,
    3,
    4,
]</fold>;
"#,
        )
    }

    #[test]
    fn fold_region() {
        check(
            r#"
// 1. some normal comment
<fold region>// region: test
// 2. some normal comment
calling_function(x,y);
// endregion: test</fold>
"#,
        )
    }

    #[test]
    fn fold_consecutive_const() {
        check(
            r#"
<fold consts>const FIRST_CONST: &str = "first";
const SECOND_CONST: &str = "second";</fold>
"#,
        )
    }

    #[test]
    fn fold_consecutive_static() {
        check(
            r#"
<fold statics>static FIRST_STATIC: &str = "first";
static SECOND_STATIC: &str = "second";</fold>
"#,
        )
    }

    #[test]
    fn fold_where_clause() {
        // fold multi-line and don't fold single line.
        check(
            r#"
fn foo()
where<fold whereclause>
    A: Foo,
    B: Foo,
    C: Foo,
    D: Foo,</fold> {}

fn bar()
where
    A: Bar, {}
"#,
        )
    }

    #[test]
    fn fold_return_type() {
        check(
            r#"
fn foo()<fold returntype>-> (
    bool,
    bool,
)</fold> { (true, true) }

fn bar() -> (bool, bool) { (true, true) }
"#,
        )
    }
}
<commit_msg>Add nested region folding test<commit_after>use rustc_hash::FxHashSet;

use syntax::{
    ast::{self, AstNode, AstToken, VisibilityOwner},
    Direction, NodeOrToken, SourceFile,
    SyntaxKind::{self, *},
    SyntaxNode, TextRange, TextSize,
};

const REGION_START: &str = "// region:";
const REGION_END: &str = "// endregion";

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Mods,
    Block,
    ArgList,
    Region,
    Consts,
    Statics,
    Array,
    WhereClause,
    ReturnType,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

// Feature: Folding
//
// Defines folding regions for curly braced blocks, runs of consecutive use, mod, const or static
// items, and `region` / `endregion` comment markers.
pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();
    let mut visited_mods = FxHashSet::default();
    let mut visited_consts = FxHashSet::default();
    let mut visited_statics = FxHashSet::default();

    // regions can be nested, here is a LIFO buffer
    let mut region_starts: Vec<TextSize> = vec![];

    for element in file.syntax().descendants_with_tokens() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(element.kind()) {
            let is_multiline = match &element {
                NodeOrToken::Node(node) => node.text().contains_char('\n'),
                NodeOrToken::Token(token) => token.text().contains('\n'),
            };
            if is_multiline {
                res.push(Fold { range: element.text_range(), kind });
                continue;
            }
        }

        match element {
            NodeOrToken::Token(token) => {
                // Fold groups of comments
                if let Some(comment) = ast::Comment::cast(token) {
                    if visited_comments.contains(&comment) {
                        continue;
                    }
                    let text = comment.text().trim_start();
                    if text.starts_with(REGION_START) {
                        region_starts.push(comment.syntax().text_range().start());
                    } else if text.starts_with(REGION_END) {
                        if let Some(region) = region_starts.pop() {
                            res.push(Fold {
                                range: TextRange::new(region, comment.syntax().text_range().end()),
                                kind: FoldKind::Region,
                            })
                        }
                    } else if let Some(range) =
                        contiguous_range_for_comment(comment, &mut visited_comments)
                    {
                        res.push(Fold { range, kind: FoldKind::Comment })
                    }
                }
            }
            NodeOrToken::Node(node) => {
                // Fold groups of imports
                if node.kind() == USE && !visited_imports.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_imports) {
                        res.push(Fold { range, kind: FoldKind::Imports })
                    }
                }

                // Fold groups of mods
                if let Some(module) = ast::Module::cast(node.clone()) {
                    if !has_visibility(&node)
                        && !visited_mods.contains(&node)
                        && module.item_list().is_none()
                    {
                        if let Some(range) = contiguous_range_for_group_unless(
                            &node,
                            has_visibility,
                            &mut visited_mods,
                        ) {
                            res.push(Fold { range, kind: FoldKind::Mods })
                        }
                    }
                }

                // Fold groups of consts
                if node.kind() == CONST && !visited_consts.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_consts) {
                        res.push(Fold { range, kind: FoldKind::Consts })
                    }
                }
                // Fold groups of consts
                if node.kind() == STATIC && !visited_statics.contains(&node) {
                    if let Some(range) = contiguous_range_for_group(&node, &mut visited_statics) {
                        res.push(Fold { range, kind: FoldKind::Statics })
                    }
                }

                // Fold where clause
                if node.kind() == WHERE_CLAUSE {
                    if let Some(range) = fold_range_for_where_clause(&node) {
                        res.push(Fold { range, kind: FoldKind::WhereClause })
                    }
                }
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        ARG_LIST | PARAM_LIST => Some(FoldKind::ArgList),
        ARRAY_EXPR => Some(FoldKind::Array),
        RET_TYPE => Some(FoldKind::ReturnType),
        ASSOC_ITEM_LIST
        | RECORD_FIELD_LIST
        | RECORD_PAT_FIELD_LIST
        | RECORD_EXPR_FIELD_LIST
        | ITEM_LIST
        | EXTERN_ITEM_LIST
        | USE_TREE_LIST
        | BLOCK_EXPR
        | MATCH_ARM_LIST
        | VARIANT_LIST
        | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_visibility(node: &SyntaxNode) -> bool {
    ast::Module::cast(node.clone()).and_then(|m| m.visibility()).is_some()
}

fn contiguous_range_for_group(
    first: &SyntaxNode,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    contiguous_range_for_group_unless(first, |_| false, visited)
}

fn contiguous_range_for_group_unless(
    first: &SyntaxNode,
    unless: impl Fn(&SyntaxNode) -> bool,
    visited: &mut FxHashSet<SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    let mut last = first.clone();
    for element in first.siblings_with_tokens(Direction::Next) {
        let node = match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                // There is a blank line or another token, which means that the
                // group ends here
                break;
            }
            NodeOrToken::Node(node) => node,
        };

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() || unless(&node) {
            break;
        }

        visited.insert(node.clone());
        last = node;
    }

    if first != &last {
        Some(TextRange::new(first.text_range().start(), last.text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment(
    first: ast::Comment,
    visited: &mut FxHashSet<ast::Comment>,
) -> Option<TextRange> {
    visited.insert(first.clone());

    // Only fold comments of the same flavor
    let group_kind = first.kind();
    if !group_kind.shape.is_line() {
        return None;
    }

    let mut last = first.clone();
    for element in first.syntax().siblings_with_tokens(Direction::Next) {
        match element {
            NodeOrToken::Token(token) => {
                if let Some(ws) = ast::Whitespace::cast(token.clone()) {
                    if !ws.spans_multiple_lines() {
                        // Ignore whitespace without blank lines
                        continue;
                    }
                }
                if let Some(c) = ast::Comment::cast(token) {
                    if c.kind() == group_kind {
                        // regions are not real comments
                        if c.text().trim().starts_with("// region:")
                            || c.text().trim().starts_with("// endregion")
                        {
                            break;
                        } else {
                            visited.insert(c.clone());
                            last = c;
                            continue;
                        }
                    }
                }
                // The comment group ends because either:
                // * An element of a different kind was reached
                // * A comment of a different flavor was reached
                break;
            }
            NodeOrToken::Node(_) => break,
        };
    }

    if first != last {
        Some(TextRange::new(first.syntax().text_range().start(), last.syntax().text_range().end()))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn fold_range_for_where_clause(node: &SyntaxNode) -> Option<TextRange> {
    let first_where_pred = node.first_child();
    let last_where_pred = node.last_child();

    if first_where_pred != last_where_pred {
        let mut it = node.descendants_with_tokens();
        if let (Some(_where_clause), Some(where_kw), Some(last_comma)) =
            (it.next(), it.next(), it.last())
        {
            let start = where_kw.text_range().end();
            let end = last_comma.text_range().end();
            return Some(TextRange::new(start, end));
        }
    }
    None
}

#[cfg(test)]
mod tests {
    use test_utils::extract_tags;

    use super::*;

    fn check(ra_fixture: &str) {
        let (ranges, text) = extract_tags(ra_fixture, "fold");

        let parse = SourceFile::parse(&text);
        let mut folds = folding_ranges(&parse.tree());
        folds.sort_by_key(|fold| (fold.range.start(), fold.range.end()));

        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );

        for (fold, (range, attr)) in folds.iter().zip(ranges.into_iter()) {
            assert_eq!(fold.range.start(), range.start(), "mismatched start of folding ranges");
            assert_eq!(fold.range.end(), range.end(), "mismatched end of folding ranges");

            let kind = match fold.kind {
                FoldKind::Comment => "comment",
                FoldKind::Imports => "imports",
                FoldKind::Mods => "mods",
                FoldKind::Block => "block",
                FoldKind::ArgList => "arglist",
                FoldKind::Region => "region",
                FoldKind::Consts => "consts",
                FoldKind::Statics => "statics",
                FoldKind::Array => "array",
                FoldKind::WhereClause => "whereclause",
                FoldKind::ReturnType => "returntype",
            };
            assert_eq!(kind, &attr.unwrap());
        }
    }

    #[test]
    fn test_fold_comments() {
        check(
            r#"
<fold comment>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold block>{
    <fold comment>// We should
    // also
    // fold
    // this one.</fold>
    <fold comment>//! But this one is different
    //! because it has another flavor</fold>
    <fold comment>/* As does this
    multiline comment */</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_imports() {
        check(
            r#"
use std::<fold block>{
    str,
    vec,
    io as iop
}</fold>;
"#,
        );
    }

    #[test]
    fn test_fold_mods() {
        check(
            r#"

pub mod foo;
<fold mods>mod after_pub;
mod after_pub_next;</fold>

<fold mods>mod before_pub;
mod before_pub_next;</fold>
pub mod bar;

mod not_folding_single;
pub mod foobar;
pub not_folding_single_next;

<fold mods>#[cfg(test)]
mod with_attribute;
mod with_attribute_next;</fold>

mod inline0 {}
mod inline1 {}

mod inline2 <fold block>{

}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_import_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

<fold imports>use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;</fold>
"#,
        );
    }

    #[test]
    fn test_fold_import_and_groups() {
        check(
            r#"
<fold imports>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold imports>use std::mem;
use std::f64;</fold>

use std::collections::<fold block>{
    HashMap,
    VecDeque,
}</fold>;
// Some random comment
"#,
        );
    }

    #[test]
    fn test_folds_structs() {
        check(
            r#"
struct Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_traits() {
        check(
            r#"
trait Foo <fold block>{
}</fold>
"#,
        );
    }

    #[test]
    fn test_folds_macros() {
        check(
            r#"
macro_rules! foo <fold block>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#,
        );
    }

    #[test]
    fn test_fold_match_arms() {
        check(
            r#"
fn main() <fold block>{
    match 0 <fold block>{
        0 => 0,
        _ => 1,
    }</fold>
}</fold>
"#,
        );
    }

    #[test]
    fn fold_big_calls() {
        check(
            r#"
fn main() <fold block>{
    frobnicate<fold arglist>(
        1,
        2,
        3,
    )</fold>
}</fold>
"#,
        )
    }

    #[test]
    fn fold_record_literals() {
        check(
            r#"
const _: S = S <fold block>{

}</fold>;
"#,
        )
    }

    #[test]
    fn fold_multiline_params() {
        check(
            r#"
fn foo<fold arglist>(
    x: i32,
    y: String,
)</fold> {}
"#,
        )
    }

    #[test]
    fn fold_multiline_array() {
        check(
            r#"
const FOO: [usize; 4] = <fold array>[
    1,
    2,
    3,
    4,
]</fold>;
"#,
        )
    }

    #[test]
    fn fold_region() {
        check(
            r#"
// 1. some normal comment
<fold region>// region: test
// 2. some normal comment
<fold region>// region: inner
fn f() {}
// endregion</fold>
fn f2() {}
// endregion: test</fold>
"#,
        )
    }

    #[test]
    fn fold_consecutive_const() {
        check(
            r#"
<fold consts>const FIRST_CONST: &str = "first";
const SECOND_CONST: &str = "second";</fold>
"#,
        )
    }

    #[test]
    fn fold_consecutive_static() {
        check(
            r#"
<fold statics>static FIRST_STATIC: &str = "first";
static SECOND_STATIC: &str = "second";</fold>
"#,
        )
    }

    #[test]
    fn fold_where_clause() {
        // fold multi-line and don't fold single line.
        check(
            r#"
fn foo()
where<fold whereclause>
    A: Foo,
    B: Foo,
    C: Foo,
    D: Foo,</fold> {}

fn bar()
where
    A: Bar, {}
"#,
        )
    }

    #[test]
    fn fold_return_type() {
        check(
            r#"
fn foo()<fold returntype>-> (
    bool,
    bool,
)</fold> { (true, true) }

fn bar() -> (bool, bool) { (true, true) }
"#,
        )
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use log;
use hostname;

use backends::Backend;
use message::{Message, WireMessage};
use errors::{Result, ErrorKind};

/// Logger for sending log-messages
///
/// A `Logger` instance can be either used as a standalone object to log directly
/// to a log-server or it can be installed as a `log`-crate log-handler (with `Logger::install`).
///
/// By default all encountered errors will be silently ignored. If you want the logger
/// to panic when an error occurs, you can change the behaviour with `Logger::enable_panic_on_error`.
pub struct Logger {
    hostname: String,
    backend: Box<Backend>,
    default_metadata: HashMap<String, String>,
    panic_on_error: bool,
}

impl Logger {
    /// Construct a new `Logger` instance
    ///
    /// The backend needs to be boxed for usage as a logger with the `log`-crate.
    /// This constructor tries to determine the local hostname (required by GELF)
    /// with the help of the `hostname`-crate. If you want to set a custom hostname
    /// check out the `Logger::new_with_hostname` constructor.
    pub fn new(backend: Box<Backend>) -> Result<Self> {
        hostname::get_hostname()
            .map(|hostname| Logger::new_with_hostname(backend, &hostname))
            .ok_or(ErrorKind::LoggerCreateFailed("Failed to determine local hostname").into())
    }

    /// Construct a new `Logger` instance with predetermined hostname
    ///
    /// The backend needs to be boxed for usage as a logger with the `log`-crate. It
    /// uses the passed hostname for the GELF `host` field
    pub fn new_with_hostname(backend: Box<Backend>, hostname: &str) -> Logger {
        Logger {
            hostname: String::from(hostname),
            backend: backend,
            default_metadata: HashMap::new(),
            panic_on_error: false,
        }
    }

    /// Install a logger instance as a `log`-Logger
    ///
    /// This method wraps `log::set_logger` as a convenience function as required
    /// by the `log`-crate. `log_level` defines the maximum log-level the logger
    /// should log.
    ///
    /// Note that installing the logger consumes it. To uninstall you need to call
    /// `log::shutdown_logger` which returns the boxed, original `Logger` instance.
    pub fn install<T: Into<log::LogLevelFilter>>(self, log_level: T) -> Result<()> {
        log::set_logger(|max_level| {
            max_level.set(Into::into(log_level));
            Box::new(self)
        })?;

        Ok(())
    }

    /// Log a message via the logger's transport to a GELF server.
    ///
    /// The logger will automatically all `default_metadata` fields to the message
    /// which are missing in the passed `Message`.
    pub fn log_message(&self, msg: Message) {
        let result = self.backend.log_message(WireMessage::new(msg, &self));

        if result.is_err() && self.panic_on_error {
            panic!(result.unwrap_err());
        }
    }

    /// Return the hostname used for GELF's `host`-field
    pub fn hostname(&self) -> &String {
        &self.hostname
    }

    /// Set the hostname used for GELF's `host`-field
    pub fn set_hostname(&mut self, hostname: String) -> &mut Self {
        self.hostname = hostname;
        self
    }

    /// Return all default metadata
    pub fn default_metadata(&self) -> &HashMap<String, String> {
        &self.default_metadata
    }

    /// Set a default metadata field
    ///
    /// Every logged `Message` is checked for every default_metadata field.
    /// If it contains an entry with the key, the default is ignored. But if
    /// there is no additional information present, the default is added to the message.
    ///
    /// This can be used for example to add a `facility` to every message:
    ///
    /// ```
    /// # use gelf::{Logger, NullBackend, Message};
    /// # let backend = NullBackend::new();
    /// # let mut logger = Logger::new(Box::new(backend)).unwrap();
    /// logger.set_default_metadata(String::from("facility"), String::from("my_awesome_rust_service"));
    ///
    /// logger.log_message(Message::new(String::from("This is important information")));
    /// // -> The message will contain an additional field "_facility" with the value "my_awesome_rust_service"
    /// ```
    pub fn set_default_metadata(&mut self, key: String, value: String) -> &mut Self {
        self.default_metadata.insert(key, value);
        self
    }

    /// Return a flag whether the logger panics when it encounters an error
    pub fn panic_on_error(&self) -> bool {
        self.panic_on_error
    }

    /// Force the logger to panic when it encounters an error
    pub fn enable_panic_on_error(&mut self) -> &mut Self {
        self.panic_on_error = true;
        self
    }

    /// Force the logger to ignore an encountered error silently
    pub fn disable_panic_on_error(&mut self) -> &mut Self {
        self.panic_on_error = false;
        self
    }
}

impl log::Log for Logger {
    /// Determines if a log message with the specified metadata would be logged.
    ///
    /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.enabled)
    /// for more details
    fn enabled(&self, _: &log::LogMetadata) -> bool {
        // The logger does not dicard any log-level by itself, therefore it is
        // always enabled
        true
    }

    /// Logs the `LogRecord`.
    /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.log)
    /// for more details
    fn log(&self, record: &log::LogRecord) {
        if !self.enabled(record.metadata()) {
            ()
        }

        self.log_message(From::from(record))
    }
}
<commit_msg>doc rewording<commit_after>use std::collections::HashMap;

use log;
use hostname;

use backends::Backend;
use message::{Message, WireMessage};
use errors::{Result, ErrorKind};

/// Logger for sending log-messages
///
/// A `Logger` instance can be either used as a standalone object to log directly
/// to a log-server or it can be installed as a `log`-crate log-handler (with `Logger::install`).
///
/// By default all encountered errors will be silently ignored. If you want the logger
/// to panic when an error occurs, you can change the behaviour with `Logger::enable_panic_on_error`.
pub struct Logger {
    hostname: String,
    backend: Box<Backend>,
    default_metadata: HashMap<String, String>,
    panic_on_error: bool,
}

impl Logger {
    /// Construct a new `Logger` instance
    ///
    /// The backend needs to be boxed for usage as a logger with the `log`-crate.
    /// This constructor tries to determine the local hostname (required by GELF)
    /// with the help of the `hostname`-crate. If you want to set a custom hostname
    /// check out the `Logger::new_with_hostname` constructor.
    pub fn new(backend: Box<Backend>) -> Result<Self> {
        hostname::get_hostname()
            .map(|hostname| Logger::new_with_hostname(backend, &hostname))
            .ok_or(ErrorKind::LoggerCreateFailed("Failed to determine local hostname").into())
    }

    /// Construct a new `Logger` instance with predetermined hostname
    ///
    /// The backend needs to be boxed for usage as a logger with the `log`-crate. It
    /// uses the passed hostname for the GELF `host` field
    pub fn new_with_hostname(backend: Box<Backend>, hostname: &str) -> Logger {
        Logger {
            hostname: String::from(hostname),
            backend: backend,
            default_metadata: HashMap::new(),
            panic_on_error: false,
        }
    }

    /// Install a logger instance as a `log`-Logger
    ///
    /// This method wraps `log::set_logger` as a convenience function as required
    /// by the `log`-crate. `log_level` defines the maximum log-level the logger
    /// should log.
    ///
    /// Note that installing the logger consumes it. To uninstall you need to call
    /// `log::shutdown_logger` which returns the boxed, original `Logger` instance.
    pub fn install<T: Into<log::LogLevelFilter>>(self, log_level: T) -> Result<()> {
        log::set_logger(|max_level| {
            max_level.set(Into::into(log_level));
            Box::new(self)
        })?;

        Ok(())
    }

    /// Log a message via the logger's transport to a GELF server.
    ///
    /// The logger will automatically add `default_metadata` fields to the message
    /// if missing in the passed `Message`.
    pub fn log_message(&self, msg: Message) {
        let result = self.backend.log_message(WireMessage::new(msg, &self));

        if result.is_err() && self.panic_on_error {
            panic!(result.unwrap_err());
        }
    }

    /// Return the hostname used for GELF's `host`-field
    pub fn hostname(&self) -> &String {
        &self.hostname
    }

    /// Set the hostname used for GELF's `host`-field
    pub fn set_hostname(&mut self, hostname: String) -> &mut Self {
        self.hostname = hostname;
        self
    }

    /// Return all default metadata
    pub fn default_metadata(&self) -> &HashMap<String, String> {
        &self.default_metadata
    }

    /// Set a default metadata field
    ///
    /// Every logged `Message` is checked for every default_metadata field.
    /// If it contains an entry with the key, the default is ignored. But if
    /// there is no additional information present, the default is added to the message.
    ///
    /// This can be used for example to add a `facility` to every message:
    ///
    /// ```
    /// # use gelf::{Logger, NullBackend, Message};
    /// # let backend = NullBackend::new();
    /// # let mut logger = Logger::new(Box::new(backend)).unwrap();
    /// logger.set_default_metadata(String::from("facility"), String::from("my_awesome_rust_service"));
    ///
    /// logger.log_message(Message::new(String::from("This is important information")));
    /// // -> The message will contain an additional field "_facility" with the value "my_awesome_rust_service"
    /// ```
    pub fn set_default_metadata(&mut self, key: String, value: String) -> &mut Self {
        self.default_metadata.insert(key, value);
        self
    }

    /// Return a flag whether the logger panics when it encounters an error
    pub fn panic_on_error(&self) -> bool {
        self.panic_on_error
    }

    /// Force the logger to panic when it encounters an error
    pub fn enable_panic_on_error(&mut self) -> &mut Self {
        self.panic_on_error = true;
        self
    }

    /// Force the logger to ignore an encountered error silently
    pub fn disable_panic_on_error(&mut self) -> &mut Self {
        self.panic_on_error = false;
        self
    }
}

impl log::Log for Logger {
    /// Determines if a log message with the specified metadata would be logged.
    ///
    /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.enabled)
    /// for more details
    fn enabled(&self, _: &log::LogMetadata) -> bool {
        // The logger does not dicard any log-level by itself, therefore it is
        // always enabled
        true
    }

    /// Logs the `LogRecord`.
    /// See [docs](https://doc.rust-lang.org/log/log/trait.Log.html#tymethod.log)
    /// for more details
    fn log(&self, record: &log::LogRecord) {
        if !self.enabled(record.metadata()) {
            ()
        }

        self.log_message(From::from(record))
    }
}
<|endoftext|>


<commit_before>/** Utility macro for implementing `OptionReflect` and generating loading functions.

## `option`-statment

Statement for regular options.

Possible forms:

```rust
option $ty { name: $name }
option $ty;
```

Fields:

* `$ty`: the type `OptionReflect` will be implemented for
* `$name`: the `&'static str` that will be returned by `OptionReflect`, defaults to `stringify!($ty)`

## `alternative`-statment

Statement for regular alternatives.

Possible forms:

```rust
alternative($opt) $ty { name: $name, init: $init }
alternative($opt) $ty { init: $init, name: $name }
alternative($opt) $ty { init: $init }
alternative($opt) $ty { name: $name }
alternative($opt) $ty;
```

Fields:

* `$ty`:
* `$opt`: the option the alternative will be added to
* `$name`: the name of the alternative
* `$init`: the `obj`-value that will be passed to `RegisterModifier::add_alternative`, defaults to
  `box <$ty as Default>::default() as Box<$opt>`

## `single`-statment

Statement for single alternative options.

Possible forms:

```rust
single $ty { name: $name, init: $init }
single $ty { init: $init, name: $name }
single $ty { init: $init }
single $ty { name: $name }
single $ty;
```

Fields:

* `$ty`: the type `OptionReflect` will be implemented for
* `$name`: the `&'static str` that will be returned by `OptionReflect`, defaults to `stringify!($ty)`
* `$init`: the `obj`-value that will be passed to `RegisterModifier::add_single`, defaults to
* `<$ty as Default>::default()`

# Example

```rust
qregister_load_fns!{

    fn load_stores(&mut RegisterModifier<Store>) => {

        single PositionStore;

        single physics::LinearVelocityStore{
            name: "LinearVelocityStore"
        }

        single ScoreStore{
            init: ScoreStore::from_file(cfg.get("score_file"))
        }

    }

    fn load_services(&mut RegisterModifier<Service>) => {

        single Timing;

        option Physics;

        alternative(Physics) nphysics::Physics{
            name: "NPhysicsPhysics"
        }

        alternative(Physics) LegacyPhysics{
            init: LegacyPhysics::new("0.1.0")
        }

    }
}

pub fn load_module(regs: &mut MyRegisters, cfg: &Cfg){

    load_stores(&mut regs.stores, cfg);
    load_services(&mut regs.services);

    register.wire_alternative("Physics", Some("LegacyPhysics"));
}
```
**/
#[macro_export]
macro_rules! qregister_load_fns {

    // 1.0 endpoint
    ($col:ident <-) => {};

    // 1.1 expansion
    ($col:ident <- single $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            impl OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.singles.push(({$name}.to_string(), Box::new($init)));

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.1 variant: $init and $name permutated
    ($col:ident <- single $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: $name, init: $init }
            $($ff)*
        }
    };

    // 1.1 variant: $name omitted, default to type-name
    ($col:ident <- single $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };

    // 1.1 variant: $init ommited, default to default-ctor
    ($col:ident <- single $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };

    // 1.1 variant: $name and $init ommitted, default type-name and default-ctor
    ($col:ident <- single $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 1.2 expansion
    ($col:ident <- option $ty:ty { name: $name:expr } $($ff:tt)*) => {
        {
            impl OptionReflect for Box<$ty> {
                fn option_name() -> &'static str { $name }
            }

            $col.options.push({$name}.to_string());

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.2 variant: $name omitted, default to type-name
    ($col:ident <- option $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- option $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 1.3 expansion
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {

            $col.alternatives.push((<Box<$opt> as ::qregister::OptionReflect>::option_name(),
                                    {$name}.to_string(),
                                    Box::new(Box::new($init) as Box<$opt>)));

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.3 variant: $init and $name permutated
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: $name, init: $init }
            $($ff)*
        }
    };

    // 1.3 variant: $name omitted, default to type-name
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };

    // 1.3 variant: $init ommited, default to default-ctor
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };

    // 1.3 variant: $name and $init ommitted, default to type-name and default-ctor
    ($col:ident <- alternative($opt:ty) $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 2.0 endpoint
    () => {};

    // 2.1 expansion
    ($(#[$mmm:meta])* fn $name:ident($reg_type:ty, $($args:ident: $arg_types:ty),*) => { $($stmts:tt)+ } $($ff:tt)*) => {
        $(#[$mmm])*
        #[allow(unused)]
        fn $name(reg: $reg_type, $($args: $arg_types),*){
            use qregister::OptionReflect;
            use std::any::Any;

            struct Collect<Base: Any + ?Sized> {
                singles: Vec<(String, Box<Base>)>,
                options: Vec<String>,
                alternatives: Vec<(&'static str, String, Box<Base>)>,
            }
            let mut col = Collect{ singles: vec![], options: vec![], alternatives: vec![] };

            qregister_load_fns!(col <- $($stmts)+);

            for (name, obj) in col.singles {
                reg.add_single(name, obj);
            }
            for name in col.options {
                reg.add_option(name);
            }
            for (opt_name, alt_name, obj) in col.alternatives {
                reg.add_alternative(opt_name, alt_name, obj);
            }
        }
        qregister_load_fns!($($ff)*);
    };

    // 2.1 variant: no `,` after $reg_type
    ($(#[$mmm:meta])* fn $name:ident($reg_type:ty) => { $($stmts:tt)+ } $($ff:tt)*) => {
        qregister_load_fns!{
            $(#[$mmm])*
            fn $name($reg_type,) => {
                $($stmts)+
            }
            $($ff)*
        }
    };
}

/*
use std::any::Any;
pub trait Base: Any + 'static {}
impl<B: ?Sized + Base> Base for Box<B> {}

pub trait SecondBase: Base {}

#[derive(Default)]
pub struct Foo;
impl Base for Foo {}

#[derive(Default)]
pub struct Bar;
impl Base for Bar {}
impl SecondBase for Bar {}

/*#[test]
pub fn test(){

    qregister_load_fns!{
        col <- single Foo{ init: Foo }
        col <- option SecondBase{ name: "2ndBase" }
        col <- alternative(SecondBase) Bar;
    }
}*/

#[derive(Default)]
pub struct Arb;
impl Base for Arb {}
impl SecondBase for Arb {}

qregister_load_fns!{
    fn test(&mut ::qregister::RegisterModifier<Base>, lel: i8) => {
        single Foo{ init: {lel + 2; Foo } }

        option SecondBase;

        alternative(SecondBase) Bar;
    }
    fn test_two(&mut ::qregister::RegisterModifier<SecondBase>) => {
        single Arb;
    }
}*/
<commit_msg>formatting<commit_after>/** Utility macro for implementing `OptionReflect` and generating loading functions.

## `option`-statment

Statement for regular options.

Possible forms:

```rust
option $ty { name: $name }
option $ty;
```

Fields:

* `$ty`: the type `OptionReflect` will be implemented for
* `$name`: the `&'static str` that will be returned by `OptionReflect`, defaults to `stringify!($ty)`

## `alternative`-statment

Statement for regular alternatives.

Possible forms:

```rust
alternative($opt) $ty { name: $name, init: $init }
alternative($opt) $ty { init: $init, name: $name }
alternative($opt) $ty { init: $init }
alternative($opt) $ty { name: $name }
alternative($opt) $ty;
```

Fields:

* `$ty`:
* `$opt`: the option the alternative will be added to
* `$name`: the name of the alternative
* `$init`: the `obj`-value that will be passed to `RegisterModifier::add_alternative`, defaults to
  `box <$ty as Default>::default() as Box<$opt>`

## `single`-statment

Statement for single alternative options.

Possible forms:

```rust
single $ty { name: $name, init: $init }
single $ty { init: $init, name: $name }
single $ty { init: $init }
single $ty { name: $name }
single $ty;
```

Fields:

* `$ty`: the type `OptionReflect` will be implemented for
* `$name`: the `&'static str` that will be returned by `OptionReflect`, defaults to `stringify!($ty)`
* `$init`: the `obj`-value that will be passed to `RegisterModifier::add_single`, defaults to
* `<$ty as Default>::default()`

# Example

```rust
qregister_load_fns!{

    fn load_stores(&mut RegisterModifier<Store>) => {

        single PositionStore;

        single physics::LinearVelocityStore{
            name: "LinearVelocityStore"
        }

        single ScoreStore{
            init: ScoreStore::from_file(cfg.get("score_file"))
        }

    }

    fn load_services(&mut RegisterModifier<Service>) => {

        single Timing;

        option Physics;

        alternative(Physics) nphysics::Physics{
            name: "NPhysicsPhysics"
        }

        alternative(Physics) LegacyPhysics{
            init: LegacyPhysics::new("0.1.0")
        }

    }
}

pub fn load_module(regs: &mut MyRegisters, cfg: &Cfg){

    load_stores(&mut regs.stores, cfg);
    load_services(&mut regs.services);

    register.wire_alternative("Physics", Some("LegacyPhysics"));
}
```
**/
#[macro_export]
macro_rules! qregister_load_fns {

    // 1.0 endpoint
    ($col:ident <-) => {};

    // 1.1 `option` expansion
    ($col:ident <- option $ty:ty { name: $name:expr } $($ff:tt)*) => {
        {
            impl OptionReflect for Box<$ty> {
                fn option_name() -> &'static str { $name }
            }

            $col.options.push({$name}.to_string());

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.1 `option` variant: $name omitted, default to type-name
    ($col:ident <- option $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- option $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 1.2 `alternative` expansion
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            $col.alternatives.push((<Box<$opt> as ::qregister::OptionReflect>::option_name(),
                                    {$name}.to_string(),
                                    Box::new(Box::new($init) as Box<$opt>)));

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.2 `alternative` variant: $init and $name permutated
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: $name, init: $init }
            $($ff)*
        }
    };

    // 1.2 `alternative` variant: $name omitted, default to type-name
    ($col:ident <- alternative($opt:ty) $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };

    // 1.2 `alternative` variant: $init ommited, default to default-ctor
    ($col:ident <- alternative($opt:ty) $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };

    // 1.2 `alternative` variant: $name and $init ommitted, default to type-name and default-ctor
    ($col:ident <- alternative($opt:ty) $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- alternative($opt) $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 1.3 `single` expansion
    ($col:ident <- single $ty:ty { name: $name:expr, init: $init:expr } $($ff:tt)*) => {
        {
            impl OptionReflect for $ty {
                fn option_name() -> &'static str { $name }
            }

            $col.singles.push(({$name}.to_string(), Box::new($init)));

            qregister_load_fns!($col <- $($ff)*);
        }
    };

    // 1.3 `single` variant: $init and $name permutated
    ($col:ident <- single $ty:ty { init: $init:expr, name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: $name, init: $init }
            $($ff)*
        }
    };

    // 1.3 `single` variant: $name omitted, default to type-name
    ($col:ident <- single $ty:ty { init: $init:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: stringify!($ty), init: $init }
            $($ff)*
        }
    };

    // 1.3 `single` variant: $init ommited, default to default-ctor
    ($col:ident <- single $ty:ty { name: $name:expr } $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: $name, init: <$ty as Default>::default() }
            $($ff)*
        }
    };

    // 1.3 `single` variant: $name and $init ommitted, default type-name and default-ctor
    ($col:ident <- single $ty:ty; $($ff:tt)*) => {
        qregister_load_fns!{
            $col <- single $ty { name: stringify!($ty) }
            $($ff)*
        }
    };

    // 2.0 endpoint
    () => {};

    // 2.1 fn expansion
    ($(#[$mmm:meta])* fn $name:ident($reg_type:ty, $($args:ident: $arg_types:ty),*) => { $($stmts:tt)+ } $($ff:tt)*) => {
        $(#[$mmm])*
        #[allow(unused)]
        fn $name(reg: $reg_type, $($args: $arg_types),*){
            use qregister::OptionReflect;
            use std::any::Any;

            struct Collect<Base: Any + ?Sized> {
                options: Vec<String>,
                alternatives: Vec<(&'static str, String, Box<Base>)>,
                singles: Vec<(String, Box<Base>)>,
            }
            let mut col = Collect{ singles: vec![], options: vec![], alternatives: vec![] };

            qregister_load_fns!(col <- $($stmts)+);

            for name in col.options {
                reg.add_option(name);
            }
            for (opt_name, alt_name, obj) in col.alternatives {
                reg.add_alternative(opt_name, alt_name, obj);
            }
            for (name, obj) in col.singles {
                reg.add_single(name, obj);
            }
        }
        qregister_load_fns!($($ff)*);
    };

    // 2.1 fn variant: no `,` after $reg_type
    ($(#[$mmm:meta])* fn $name:ident($reg_type:ty) => { $($stmts:tt)+ } $($ff:tt)*) => {
        qregister_load_fns!{
            $(#[$mmm])*
            fn $name($reg_type,) => {
                $($stmts)+
            }
            $($ff)*
        }
    };
}

/*
use std::any::Any;
pub trait Base: Any + 'static {}
impl<B: ?Sized + Base> Base for Box<B> {}

pub trait SecondBase: Base {}

#[derive(Default)]
pub struct Foo;
impl Base for Foo {}

#[derive(Default)]
pub struct Bar;
impl Base for Bar {}
impl SecondBase for Bar {}

/*#[test]
pub fn test(){

    qregister_load_fns!{
        col <- single Foo{ init: Foo }
        col <- option SecondBase{ name: "2ndBase" }
        col <- alternative(SecondBase) Bar;
    }
}*/

#[derive(Default)]
pub struct Arb;
impl Base for Arb {}
impl SecondBase for Arb {}

qregister_load_fns!{
    fn test(&mut ::qregister::RegisterModifier<Base>, lel: i8) => {
        single Foo{ init: {lel + 2; Foo } }

        option SecondBase;

        alternative(SecondBase) Bar;
    }
    fn test_two(&mut ::qregister::RegisterModifier<SecondBase>) => {
        single Arb;
    }
}*/
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    dead_code,
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
#[macro_use] extern crate itertools;
#[cfg(unix)] extern crate xdg_basedir;
extern crate env_logger;
extern crate tempfile;
extern crate ansi_term;

extern crate clap;
extern crate toml;

extern crate libimagstore;
extern crate libimagstorestdhook;
extern crate libimagutil;
#[macro_use] extern crate libimagerror;

pub mod error;
pub mod configuration;
pub mod logger;
pub mod runtime;
pub mod setup;

<commit_msg>libimagrt: Remove unused imports<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#![deny(
    dead_code,
    non_camel_case_types,
    non_snake_case,
    path_statements,
    trivial_numeric_casts,
    unstable_features,
    unused_allocation,
    unused_import_braces,
    unused_imports,
    unused_must_use,
    unused_mut,
    unused_qualifications,
    while_true,
)]

#[macro_use] extern crate log;
extern crate itertools;
#[cfg(unix)] extern crate xdg_basedir;
extern crate env_logger;
extern crate tempfile;
extern crate ansi_term;

extern crate clap;
extern crate toml;

extern crate libimagstore;
extern crate libimagstorestdhook;
extern crate libimagutil;
#[macro_use] extern crate libimagerror;

pub mod error;
pub mod configuration;
pub mod logger;
pub mod runtime;
pub mod setup;

<|endoftext|>


<commit_before>
use math::sqrtf;
use utils::Point3;
use motor::Motor;
use hardware::peripherals::time::Time;

pub trait MotionPlanner {
    fn set_target(&mut self, target: Point3);
    fn update(&mut self);
}

pub struct CartesianMotionPlanner<'a> {
    x_motor: &'a mut Motor,
    y_motor: &'a mut Motor,
    z_motor: &'a mut Motor,
    time: &'a Time,
    max_acceleration: f32,
    max_speed: f32,
    start_speed: f32,
    end_speed: f32,
    motion_distance: f32,
    delta_x: f32,
    delta_y: f32,
    delta_z: f32,
    top_x_speed: f32,
    top_y_speed: f32,
    top_z_speed: f32,
    start_time: u32,
    transition1: u32,
    transition2: u32,
    end_time: u32,
    set_start: bool,
}

impl<'a> CartesianMotionPlanner<'a> {
    fn new(
        x_motor: &'a mut Motor,
        y_motor: &'a mut Motor,
        z_motor: &'a mut Motor,
        time: &'a Time,
        max_acceleration: f32,
        max_speed: f32,
        start_speed: f32,
        end_speed: f32,
    ) -> CartesianMotionPlanner<'a> {
        CartesianMotionPlanner {
            x_motor: x_motor,
            y_motor: y_motor,
            z_motor: z_motor,
            time: time,
            max_acceleration: max_acceleration,
            max_speed: max_speed,
            start_speed: start_speed,
            end_speed: end_speed,
            motion_distance: 0.0,
            delta_x: 0.0,
            delta_y: 0.0,
            delta_z: 0.0,
            top_x_speed: 0.0,
            top_y_speed: 0.0,
            top_z_speed: 0.0,
            start_time: 0,
            transition1: 0,
            transition2: 0,
            end_time: 0,
            set_start: false,
        }
    }

    fn get_axis_top_speed(&self, delta: f32, top_speed: f32) -> f32 {
        let speedup_distance = top_speed / self.max_acceleration;
        if speedup_distance < delta / 2.0 {
            (delta / 2.0) * self.max_acceleration
        } else {
            top_speed
        }
    }

    #[inline]
    fn translate_to_axis(&self, delta: f32, axis_speed: f32) -> f32 {
        (axis_speed * delta) / self.motion_distance
    }

    fn get_current_position(&self) -> Point3 {
        Point3::new(
            self.x_motor.get_position(),
            self.y_motor.get_position(),
            self.z_motor.get_position(),
        )
    }

    fn get_current_velocity(&self) -> Point3 {
        Point3::new(
            self.x_motor.get_velocity(),
            self.y_motor.get_velocity(),
            self.z_motor.get_velocity(),
        )
    }
}

impl<'a> MotionPlanner for CartesianMotionPlanner<'a> {
    fn set_target(&mut self, point: Point3) {
        let current_position = self.get_current_position();
        self.delta_x = point.x - current_position.x;
        self.delta_y = point.y - current_position.y;
        self.delta_z = point.z - current_position.z;
        self.motion_distance = sqrtf(
            self.delta_x * self.delta_x + self.delta_y * self.delta_y + self.delta_z * self.delta_z,
        );
        self.top_x_speed = self.translate_to_axis(
            self.delta_x,
            self.get_axis_top_speed(self.delta_x, self.max_speed),
        );
        self.top_y_speed = self.translate_to_axis(
            self.delta_y,
            self.get_axis_top_speed(self.delta_y, self.max_speed),
        );
        self.top_z_speed = self.translate_to_axis(
            self.delta_z,
            self.get_axis_top_speed(self.delta_z, self.max_speed),
        );
        self.transition1 = ((self.max_speed - self.start_speed) / self.max_acceleration) as u32;
        let part1_distance = self.start_speed * self.transition1 as f32 +
            (1.0 / 2.0) * self.max_acceleration * (self.transition1 * self.transition1) as f32;
        let part3_time = ((self.max_speed - self.end_speed) / self.max_acceleration) as u32;
        let part3_distance = self.start_speed * part3_time as f32 +
            (1.0 / 2.0) * self.max_acceleration * (part3_time * part3_time) as f32;
        self.transition2 =
            (self.motion_distance - (part1_distance + part3_distance) / self.max_speed) as u32;
        self.end_time = self.transition2 + part3_time;
        self.set_start = true;
    }

    fn update(&mut self) {
        if self.set_start {
            self.start_time = self.time.now();
            self.set_start = false;
        }
        let now = self.time.now() - self.start_time;
        // TODO account for now is negative?
        let current_speed;
        if now < self.transition1 {
            current_speed = now as f32 * self.max_acceleration + self.start_speed as f32;
        } else if now >= self.transition1 && now < self.transition2 {
            current_speed = self.max_speed;
        } else if now >= self.transition2 && now < self.end_time {
            current_speed = now as f32 * self.max_acceleration * -1.0 + self.max_speed;
        } else {
            current_speed = self.end_speed;
        }
        // TODO Handle set_current_velocity failure.
        self.x_motor
            .set_velocity((current_speed * self.delta_x) / self.motion_distance);
        self.y_motor
            .set_velocity((current_speed * self.delta_y) / self.motion_distance);
        self.z_motor
            .set_velocity((current_speed * self.delta_z) / self.motion_distance);
    }
}
<commit_msg>Make CartesionMotionPlanner::new pub<commit_after>
use math::sqrtf;
use utils::Point3;
use motor::Motor;
use hardware::peripherals::time::Time;

pub trait MotionPlanner {
    fn set_target(&mut self, target: Point3);
    fn update(&mut self);
}

pub struct CartesianMotionPlanner<'a> {
    x_motor: &'a mut Motor,
    y_motor: &'a mut Motor,
    z_motor: &'a mut Motor,
    time: &'a Time,
    max_acceleration: f32,
    max_speed: f32,
    start_speed: f32,
    end_speed: f32,
    motion_distance: f32,
    delta_x: f32,
    delta_y: f32,
    delta_z: f32,
    top_x_speed: f32,
    top_y_speed: f32,
    top_z_speed: f32,
    start_time: u32,
    transition1: u32,
    transition2: u32,
    end_time: u32,
    set_start: bool,
}

impl<'a> CartesianMotionPlanner<'a> {
    pub fn new(
        x_motor: &'a mut Motor,
        y_motor: &'a mut Motor,
        z_motor: &'a mut Motor,
        time: &'a Time,
        max_acceleration: f32,
        max_speed: f32,
        start_speed: f32,
        end_speed: f32,
    ) -> CartesianMotionPlanner<'a> {
        CartesianMotionPlanner {
            x_motor: x_motor,
            y_motor: y_motor,
            z_motor: z_motor,
            time: time,
            max_acceleration: max_acceleration,
            max_speed: max_speed,
            start_speed: start_speed,
            end_speed: end_speed,
            motion_distance: 0.0,
            delta_x: 0.0,
            delta_y: 0.0,
            delta_z: 0.0,
            top_x_speed: 0.0,
            top_y_speed: 0.0,
            top_z_speed: 0.0,
            start_time: 0,
            transition1: 0,
            transition2: 0,
            end_time: 0,
            set_start: false,
        }
    }

    fn get_axis_top_speed(&self, delta: f32, top_speed: f32) -> f32 {
        let speedup_distance = top_speed / self.max_acceleration;
        if speedup_distance < delta / 2.0 {
            (delta / 2.0) * self.max_acceleration
        } else {
            top_speed
        }
    }

    #[inline]
    fn translate_to_axis(&self, delta: f32, axis_speed: f32) -> f32 {
        (axis_speed * delta) / self.motion_distance
    }

    fn get_current_position(&self) -> Point3 {
        Point3::new(
            self.x_motor.get_position(),
            self.y_motor.get_position(),
            self.z_motor.get_position(),
        )
    }

    fn get_current_velocity(&self) -> Point3 {
        Point3::new(
            self.x_motor.get_velocity(),
            self.y_motor.get_velocity(),
            self.z_motor.get_velocity(),
        )
    }
}

impl<'a> MotionPlanner for CartesianMotionPlanner<'a> {
    fn set_target(&mut self, point: Point3) {
        let current_position = self.get_current_position();
        self.delta_x = point.x - current_position.x;
        self.delta_y = point.y - current_position.y;
        self.delta_z = point.z - current_position.z;
        self.motion_distance = sqrtf(
            self.delta_x * self.delta_x + self.delta_y * self.delta_y + self.delta_z * self.delta_z,
        );
        self.top_x_speed = self.translate_to_axis(
            self.delta_x,
            self.get_axis_top_speed(self.delta_x, self.max_speed),
        );
        self.top_y_speed = self.translate_to_axis(
            self.delta_y,
            self.get_axis_top_speed(self.delta_y, self.max_speed),
        );
        self.top_z_speed = self.translate_to_axis(
            self.delta_z,
            self.get_axis_top_speed(self.delta_z, self.max_speed),
        );
        self.transition1 = ((self.max_speed - self.start_speed) / self.max_acceleration) as u32;
        let part1_distance = self.start_speed * self.transition1 as f32 +
            (1.0 / 2.0) * self.max_acceleration * (self.transition1 * self.transition1) as f32;
        let part3_time = ((self.max_speed - self.end_speed) / self.max_acceleration) as u32;
        let part3_distance = self.start_speed * part3_time as f32 +
            (1.0 / 2.0) * self.max_acceleration * (part3_time * part3_time) as f32;
        self.transition2 =
            (self.motion_distance - (part1_distance + part3_distance) / self.max_speed) as u32;
        self.end_time = self.transition2 + part3_time;
        self.set_start = true;
    }

    fn update(&mut self) {
        if self.set_start {
            self.start_time = self.time.now();
            self.set_start = false;
        }
        let now = self.time.now() - self.start_time;
        // TODO account for now is negative?
        let current_speed;
        if now < self.transition1 {
            current_speed = now as f32 * self.max_acceleration + self.start_speed as f32;
        } else if now >= self.transition1 && now < self.transition2 {
            current_speed = self.max_speed;
        } else if now >= self.transition2 && now < self.end_time {
            current_speed = now as f32 * self.max_acceleration * -1.0 + self.max_speed;
        } else {
            current_speed = self.end_speed;
        }
        // TODO Handle set_current_velocity failure.
        self.x_motor
            .set_velocity((current_speed * self.delta_x) / self.motion_distance);
        self.y_motor
            .set_velocity((current_speed * self.delta_y) / self.motion_distance);
        self.z_motor
            .set_velocity((current_speed * self.delta_z) / self.motion_distance);
    }
}
<|endoftext|>