<commit_before>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::collections::HashMap;
use std::io;
use std::path::PathBuf;

pub struct FilesWatcher {
    inotify: INotify,
    watches: HashMap<Watch, PathBuf>
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().ok().expect(
            "Fatal Error: Could not initialize inotify."
        );

        FilesWatcher { 
            inotify: inotify,
            watches: HashMap::new()
        }
    }

    pub fn add_file(&mut self, path: PathBuf) {
        let watch_id = self.inotify.add_watch(&path, IN_MODIFY | IN_DELETE);

        if watch_id.is_ok() {
           self.watches.insert(watch_id.unwrap(), path);
        } else {
            println!("Error adding watch for file: {:?}", watch_id.err());
        }
    }

    pub fn wait_for_events(&mut self) -> io::Result<Vec<EventPath>> {
        let events = try!(self.inotify.wait_for_events());
        
        let mut event_paths: Vec<EventPath> = vec![];
        for event in events.iter() {
            let path = self.watches.get(&event.wd);
            if path.is_some() {
                event_paths.push(EventPath::new(
                    path.unwrap().clone(),
                    event.clone()
                ));
            } else {
                println!("Error: no path for watch: {:?}", event.wd);
            }
        }

        Ok(event_paths)
    }

    pub fn close(&mut self) {
        self.inotify.close().ok().expect(
            "Fatal Error: Could not close inotify."
        );
    }
}

pub struct EventPath {
    event: Event,
    path: PathBuf
}

impl EventPath {
    fn new(path: PathBuf, event: Event) -> EventPath {
        EventPath {
            event: event,
            path: path
        }
    }
}


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

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();
        let filepath = path.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path);

        write_to(&mut file);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(1, event_paths.len());
            for event_path in event_paths.iter() {
                assert_eq!(filepath, event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }


    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let filepath1 = path1.clone();
        let filepath2 = path2.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path1);
        fw.add_file(path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(2, event_paths.len());
            for event_path in event_paths.iter() {
                assert!(filepath1 == event_path.path || filepath2 == event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()<commit_msg>Give out borrows in EventPaths<commit_after>extern crate inotify;
extern crate libc;

use inotify::INotify;
use inotify::wrapper::Event;
use inotify::wrapper::Watch;
use inotify::ffi::*;

use std::collections::HashMap;
use std::io;
use std::path::PathBuf;

pub struct FilesWatcher {
    inotify: INotify,
    watches: HashMap<Watch, PathBuf>
}

impl FilesWatcher {
    pub fn new() -> FilesWatcher {
        let inotify = INotify::init().ok().expect(
            "Fatal Error: Could not initialize inotify."
        );

        FilesWatcher { 
            inotify: inotify,
            watches: HashMap::new()
        }
    }

    pub fn add_file(&mut self, path: PathBuf) {
        let watch_id = self.inotify.add_watch(&path, IN_MODIFY | IN_DELETE);

        if watch_id.is_ok() {
           self.watches.insert(watch_id.unwrap(), path);
        } else {
            println!("Error adding watch for file: {:?}", watch_id.err());
        }
    }

    pub fn wait_for_events(&mut self) -> io::Result<Vec<EventPath>> {
        let events = try!(self.inotify.wait_for_events());
        
        let mut event_paths: Vec<EventPath> = vec![];
        for event in events.iter() {
            let path = self.watches.get(&event.wd);
            if path.is_some() {
                event_paths.push(EventPath::new(
                    path.unwrap(),
                    event
                ));
            } else {
                println!("Error: no path for watch: {:?}", event.wd);
            }
        }

        Ok(event_paths)
    }

    pub fn close(&mut self) {
        self.inotify.close().ok().expect(
            "Fatal Error: Could not close inotify."
        );
    }
}

pub struct EventPath<'a> {
    event: &'a Event,
    path: &'a PathBuf
}

impl <'a>EventPath<'a> {
    fn new(path: &'a PathBuf, event: &'a Event) -> EventPath<'a> {
        EventPath {
            event: event,
            path: path
        }
    }
}


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

    use super::*;

    use self::rand::{thread_rng, Rng};
    use std::env::temp_dir;
    use std::fs::File;
    use std::fs::remove_file;
    use std::io::Write;
    use std::path::Path;
    use std::path::PathBuf;

    #[test]
    fn watch_a_single_file() {
        let (path, mut file) = create_temp_file();
        let filepath = path.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path);

        write_to(&mut file);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(1, event_paths.len());
            for event_path in event_paths.iter() {
                assert_eq!(&filepath, event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }


    #[test]
    fn watch_two_files() {
        let (path1, mut file1) = create_temp_file();
        let (path2, mut file2) = create_temp_file();

        let filepath1 = path1.clone();
        let filepath2 = path2.clone();

        let mut fw = FilesWatcher::new();
        fw.add_file(path1);
        fw.add_file(path2);

        write_to(&mut file1);
        write_to(&mut file2);

        {
            let event_paths = fw.wait_for_events().unwrap();
            assert_eq!(2, event_paths.len());
            for event_path in event_paths.iter() {
                assert!(&filepath1 == event_path.path || &filepath2 == event_path.path);
            }
        }

        for path in fw.watches.values() {
            remove_temp_file(path);
        }
        fw.close();
    }

    fn create_temp_file() -> (PathBuf, File) {
        let rand_part: String = thread_rng()
{'fraction_non_alphanumeric': 0.13556771545827634, 'fraction_numerical': 0.004377564979480164, 'mean_word_length': 1.8977407847800238, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1508763', 'n_tokens_mistral': 2395, 'n_tokens_neox': 2332, 'n_words': 503}