<commit_before>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
    time::Duration,
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
    duration: Duration,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            Some("text") | Some("application") | Some("image") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        let file_name = cb.to_str().unwrap();
        // todo: only one file handle for all libs, no read_from_path, etc but one and then handle
        id3tag::read_from_path(file_name)
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e| match mp4tag::read_from_path(file_name) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(err) => Err(err),
            })
            .or_else(|e| {
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
}
<commit_msg>[collection] collect error messages<commit_after>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
    time::Duration,
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
    duration: Duration,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            Some("text") | Some("application") | Some("image") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        let file_name = cb.to_str().unwrap();
        // todo: only one file handle for all libs, no read_from_path, etc but one and then handle
        id3tag::read_from_path(file_name)
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e| match mp4tag::read_from_path(file_name) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(err) => Err((e, err)),
            })
            .or_else(|e| {
                let (id3tag_error, mp4ameta_error) = e;
                error!("path: {}", file_name);
                error!("id3tag  : {:?}", id3tag_error);
                error!("mp4ameta: {:?}", mp4ameta_error);
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! TODO Documentation
use std::{fmt, panic};
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};

use errors::{UpgradeHandleErr, UpgradeHandleResult};
use wlroots_sys::{wlr_input_device, wlr_key_state, wlr_keyboard, wlr_keyboard_get_modifiers,
                  wlr_keyboard_led, wlr_keyboard_led_update, wlr_keyboard_modifier,
                  wlr_keyboard_set_keymap};

use xkbcommon::xkb::{self, Keycode, Keymap, LedIndex, ModIndex, ModMask};

use InputDevice;

pub type KeyState = wlr_key_state;

/// The masks for each group of modifiers.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ModifierMasks {
    pub depressed: ModMask,
    pub latched: ModMask,
    pub locked: ModMask,
    pub group: ModMask
}

/// Information about repeated keypresses for a particular Keyboard.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct RepeatInfo {
    /// The rate at which extended keypresses will fire more events.
    rate: i32,
    /// How long it takes for a keypress to register on this device.
    delay: i32
}

pub struct Keyboard {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `KeyboardHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `KeyboardHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The device that refers to this keyboard.
    device: InputDevice,
    /// The underlying keyboard data.
    keyboard: *mut wlr_keyboard
}

pub struct KeyboardHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the keyboard associated with this handle,
    handle: Weak<AtomicBool>,
    /// The device that refers to this keyboard.
    device: InputDevice,
    /// The underlying keyboard data.
    keyboard: *mut wlr_keyboard
}

impl Keyboard {
    /// Tries to convert an input device to a Keyboard
    ///
    /// Returns None if it is of a different type of input variant.
    ///
    /// # Safety
    /// This creates a totally new Keyboard (e.g with its own reference count)
    /// so only do this once per `wlr_input_device`!
    pub(crate) unsafe fn new_from_input_device(device: *mut wlr_input_device) -> Option<Self> {
        use wlroots_sys::wlr_input_device_type::*;
        match (*device).type_ {
            WLR_INPUT_DEVICE_KEYBOARD => {
                let keyboard = (*device).__bindgen_anon_1.keyboard;
                Some(Keyboard { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                device: InputDevice::from_ptr(device),
                                keyboard })
            }
            _ => None
        }
    }

    unsafe fn from_handle(handle: &KeyboardHandle) -> Self {
        Keyboard { liveliness: None,
                   device: handle.input_device().clone(),
                   keyboard: handle.as_ptr() }
    }

    /// Gets the wlr_keyboard associated with this KeyboardHandle.
    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_keyboard {
        self.keyboard
    }

    /// Gets the wlr_input_device associated with this KeyboardHandle
    pub fn input_device(&self) -> &InputDevice {
        &self.device
    }

    /// Set the keymap for this Keyboard.
    pub fn set_keymap(&mut self, keymap: &Keymap) {
        unsafe {
            // NOTE wlr_keyboard_set_keymap updates the reference count,
            // so we don't need to mem::forget the key map here
            // or take it by value.
            wlr_keyboard_set_keymap(self.keyboard, keymap.get_raw_ptr() as _);
        }
    }

    /// Get the XKB keymap associated with this Keyboard.
    pub fn get_keymap(&mut self) -> Option<Keymap> {
        unsafe {
            let keymap_ptr = (*self.keyboard).keymap as *mut _;
            if keymap_ptr.is_null() {
                None
            } else {
                Some(Keymap::from_raw_ptr(keymap_ptr))
            }
        }
    }

    /// Get the keycodes for this keyboard as reported by XKB.
    ///
    /// # Limitations
    /// wlroots limits this list to `WLR_KEYBOARD_KEYS_CAP` elements,
    /// which at the time of writing is `32`.
    pub fn keycodes(&self) -> Vec<Keycode> {
        unsafe {
            let mut result = (*self.keyboard).keycodes.to_vec();
            result.truncate((*self.keyboard).num_keycodes);
            result
        }
    }

    /// Get the list of LEDs for this keyboard as reported by XKB.
    pub fn led_list(&self) -> &[LedIndex] {
        unsafe { &(*self.keyboard).led_indexes }
    }

    /// Get the list of modifiers for this keyboard as reported by XKB.
    pub fn modifier_list(&self) -> &[ModIndex] {
        unsafe { &(*self.keyboard).mod_indexes }
    }

    /// Get the size of the keymap.
    pub fn keymap_size(&self) -> usize {
        unsafe { (*self.keyboard).keymap_size }
    }

    /// Get the XKB state associated with this `Keyboard`.
    pub fn get_xkb_state(&mut self) -> Option<xkb::State> {
        unsafe {
            let xkb_state_ptr = (*self.keyboard).xkb_state as *mut _;
            if xkb_state_ptr.is_null() {
                None
            } else {
                Some(xkb::State::from_raw_ptr(xkb_state_ptr))
            }
        }
    }

    /// Get the repeat info for this keyboard.
    pub fn repeat_info(&self) -> RepeatInfo {
        unsafe {
            RepeatInfo { rate: (*self.keyboard).repeat_info.rate,
                         delay: (*self.keyboard).repeat_info.delay }
        }
    }

    /// Update the LED lights using the provided bitmap.
    ///
    /// 1 means one, 0 means off.
    pub fn update_led(&mut self, leds: KeyboardLed) {
        unsafe {
            wlr_keyboard_led_update(self.keyboard, leds.bits() as u32);
        }
    }

    /// Get the modifiers that are currently pressed on the keyboard.
    pub fn get_modifiers(&self) -> KeyboardModifier {
        unsafe {
            KeyboardModifier::from_bits_truncate(wlr_keyboard_get_modifiers(self.keyboard))
        }
    }

    /// Get the modifier masks for each group.
    pub fn get_modifier_masks(&self) -> ModifierMasks {
        unsafe {
            ModifierMasks { depressed: (*self.keyboard).modifiers.depressed,
                            latched: (*self.keyboard).modifiers.latched,
                            locked: (*self.keyboard).modifiers.locked,
                            group: (*self.keyboard).modifiers.group }
        }
    }

    /// Creates a weak reference to a `Keyboard`.
    ///
    /// # Panics
    /// If this `Keyboard` is a previously upgraded `KeyboardHandle`,
    /// then this function will panic.
    pub fn weak_reference(&self) -> KeyboardHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade previously upgraded KeyboardHandle!");
        KeyboardHandle { handle: Rc::downgrade(arc),
                         // NOTE Rationale for cloning:
                         // We can't use the keyboard handle unless the keyboard is alive,
                         // which means the device pointer is still alive.
                         device: unsafe { self.device.clone() },
                         keyboard: self.keyboard }
    }

    /// Manually set the lock used to determine if a double-borrow is
    /// occuring on this structure.
    ///
    /// # Panics
    /// Panics when trying to set the lock on an upgraded handle.
    pub(crate) unsafe fn set_lock(&self, val: bool) {
        self.liveliness.as_ref()
            .expect("Tried to set lock on borrowed Keyboard")
            .store(val, Ordering::Release);
    }
}

impl Drop for Keyboard {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    wlr_log!(L_DEBUG, "Dropped Keyboard {:p}", self.keyboard);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to Keyboard {:p}",
                                 weak_count,
                                 self.keyboard);
                    }
                }
            }
        }
    }
}

impl fmt::Debug for Keyboard {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f,
               "Keyboard {{ device: {:?}, keyboard: {:?} }}",
               self.device, self.keyboard)
    }
}

impl KeyboardHandle {
    /// Upgrades the keyboard handle to a reference to the backing `Keyboard`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbounded `Keyboard`
    /// which may live forever..
    /// But no keyboard lives forever and might be disconnected at any time.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<Keyboard> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::DoubleUpgrade)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let keyboard = Keyboard::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(keyboard)
            })
    }

    /// Run a function on the referenced Keyboard, if it still exists
    ///
    /// Returns the result of the function, if successful
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the output
    /// to a short lived scope of an anonymous function,
    /// this function ensures the Keyboard does not live longer
    /// than it exists.
    ///
    /// # Panics
    /// This function will panic if multiple mutable borrows are detected.
    /// This will happen if you call `upgrade` directly within this callback,
    /// or if you run this function within the another run to the same `Output`.
    ///
    /// So don't nest `run` calls and everything will be ok :).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<Option<R>>
        where F: FnOnce(&mut Keyboard) -> R
    {
        let mut keyboard = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| Some(runner(&mut keyboard))));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running keyboard callback, mutable \
                                                    lock was false for: {:?}",
                                                   keyboard);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    /// Gets the wlr_input_device associated with this KeyboardHandle
    pub fn input_device(&self) -> &InputDevice {
        &self.device
    }

    /// Gets the wlr_keyboard associated with this KeyboardHandle.
    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_keyboard {
        self.keyboard
    }
}

impl fmt::Debug for KeyboardHandle {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f,
               "Keyboard {{ device: {:?}, keyboard: {:?} }}",
               self.device, self.keyboard)
    }
}

bitflags! {
    pub struct KeyboardLed: u32 {
        const WLR_LED_NUM_LOCK = wlr_keyboard_led::WLR_LED_NUM_LOCK as u32;
        const WLR_LED_CAPS_LOCK = wlr_keyboard_led::WLR_LED_CAPS_LOCK as u32;
        const WLR_LED_SCROLL_LOCK = wlr_keyboard_led::WLR_LED_SCROLL_LOCK as u32;
    }
}

bitflags! {
    pub struct KeyboardModifier: u32 {
        const WLR_MODIFIER_SHIFT = wlr_keyboard_modifier::WLR_MODIFIER_SHIFT as u32;
        const WLR_MODIFIER_CAPS = wlr_keyboard_modifier::WLR_MODIFIER_CAPS as u32;
        const WLR_MODIFIER_CTRL = wlr_keyboard_modifier::WLR_MODIFIER_CTRL as u32;
        const WLR_MODIFIER_ALT = wlr_keyboard_modifier::WLR_MODIFIER_ALT as u32;
        const WLR_MODIFIER_MOD2 = wlr_keyboard_modifier::WLR_MODIFIER_MOD2 as u32;
        const WLR_MODIFIER_MOD3 = wlr_keyboard_modifier::WLR_MODIFIER_MOD3 as u32;
        const WLR_MODIFIER_LOGO = wlr_keyboard_modifier::WLR_MODIFIER_LOGO as u32;
        const WLR_MODIFIER_MOD5 = wlr_keyboard_modifier::WLR_MODIFIER_MOD5 as u32;
    }
}

impl fmt::Display for KeyboardModifier {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mod_vec = vec![("Shift", KeyboardModifier::WLR_MODIFIER_SHIFT),
                           ("Caps lock", KeyboardModifier::WLR_MODIFIER_CAPS),
                           ("Ctrl", KeyboardModifier::WLR_MODIFIER_CTRL),
                           ("Alt", KeyboardModifier::WLR_MODIFIER_ALT),
                           ("Mod2", KeyboardModifier::WLR_MODIFIER_MOD2),
                           ("Mod3", KeyboardModifier::WLR_MODIFIER_MOD3),
                           ("Logo", KeyboardModifier::WLR_MODIFIER_LOGO),
                           ("Mod5", KeyboardModifier::WLR_MODIFIER_MOD5)];

        let mods: Vec<&str> = mod_vec.into_iter()
                                     .filter(|&(_, flag)| self.contains(flag))
                                     .map(|(st, _)| st)
                                     .collect();

        write!(formatter, "{:?}", mods)
    }
}
<commit_msg>Removed explicit debug impl on keyboard/handle<commit_after>//! TODO Documentation
use std::{fmt, panic};
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};

use errors::{UpgradeHandleErr, UpgradeHandleResult};
use wlroots_sys::{wlr_input_device, wlr_key_state, wlr_keyboard, wlr_keyboard_get_modifiers,
                  wlr_keyboard_led, wlr_keyboard_led_update, wlr_keyboard_modifier,
                  wlr_keyboard_set_keymap};

use xkbcommon::xkb::{self, Keycode, Keymap, LedIndex, ModIndex, ModMask};

use InputDevice;

pub type KeyState = wlr_key_state;

/// The masks for each group of modifiers.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct ModifierMasks {
    pub depressed: ModMask,
    pub latched: ModMask,
    pub locked: ModMask,
    pub group: ModMask
}

/// Information about repeated keypresses for a particular Keyboard.
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct RepeatInfo {
    /// The rate at which extended keypresses will fire more events.
    rate: i32,
    /// How long it takes for a keypress to register on this device.
    delay: i32
}

#[derive(Debug)]
pub struct Keyboard {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `KeyboardHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `KeyboardHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The device that refers to this keyboard.
    device: InputDevice,
    /// The underlying keyboard data.
    keyboard: *mut wlr_keyboard
}

#[derive(Debug)]
pub struct KeyboardHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the keyboard associated with this handle,
    handle: Weak<AtomicBool>,
    /// The device that refers to this keyboard.
    device: InputDevice,
    /// The underlying keyboard data.
    keyboard: *mut wlr_keyboard
}

impl Keyboard {
    /// Tries to convert an input device to a Keyboard
    ///
    /// Returns None if it is of a different type of input variant.
    ///
    /// # Safety
    /// This creates a totally new Keyboard (e.g with its own reference count)
    /// so only do this once per `wlr_input_device`!
    pub(crate) unsafe fn new_from_input_device(device: *mut wlr_input_device) -> Option<Self> {
        use wlroots_sys::wlr_input_device_type::*;
        match (*device).type_ {
            WLR_INPUT_DEVICE_KEYBOARD => {
                let keyboard = (*device).__bindgen_anon_1.keyboard;
                Some(Keyboard { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                device: InputDevice::from_ptr(device),
                                keyboard })
            }
            _ => None
        }
    }

    unsafe fn from_handle(handle: &KeyboardHandle) -> Self {
        Keyboard { liveliness: None,
                   device: handle.input_device().clone(),
                   keyboard: handle.as_ptr() }
    }

    /// Gets the wlr_keyboard associated with this KeyboardHandle.
    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_keyboard {
        self.keyboard
    }

    /// Gets the wlr_input_device associated with this KeyboardHandle
    pub fn input_device(&self) -> &InputDevice {
        &self.device
    }

    /// Set the keymap for this Keyboard.
    pub fn set_keymap(&mut self, keymap: &Keymap) {
        unsafe {
            // NOTE wlr_keyboard_set_keymap updates the reference count,
            // so we don't need to mem::forget the key map here
            // or take it by value.
            wlr_keyboard_set_keymap(self.keyboard, keymap.get_raw_ptr() as _);
        }
    }

    /// Get the XKB keymap associated with this Keyboard.
    pub fn get_keymap(&mut self) -> Option<Keymap> {
        unsafe {
            let keymap_ptr = (*self.keyboard).keymap as *mut _;
            if keymap_ptr.is_null() {
                None
            } else {
                Some(Keymap::from_raw_ptr(keymap_ptr))
            }
        }
    }

    /// Get the keycodes for this keyboard as reported by XKB.
    ///
    /// # Limitations
    /// wlroots limits this list to `WLR_KEYBOARD_KEYS_CAP` elements,
    /// which at the time of writing is `32`.
    pub fn keycodes(&self) -> Vec<Keycode> {
        unsafe {
            let mut result = (*self.keyboard).keycodes.to_vec();
            result.truncate((*self.keyboard).num_keycodes);
            result
        }
    }

    /// Get the list of LEDs for this keyboard as reported by XKB.
    pub fn led_list(&self) -> &[LedIndex] {
        unsafe { &(*self.keyboard).led_indexes }
    }

    /// Get the list of modifiers for this keyboard as reported by XKB.
    pub fn modifier_list(&self) -> &[ModIndex] {
        unsafe { &(*self.keyboard).mod_indexes }
    }

    /// Get the size of the keymap.
    pub fn keymap_size(&self) -> usize {
        unsafe { (*self.keyboard).keymap_size }
    }

    /// Get the XKB state associated with this `Keyboard`.
    pub fn get_xkb_state(&mut self) -> Option<xkb::State> {
        unsafe {
            let xkb_state_ptr = (*self.keyboard).xkb_state as *mut _;
            if xkb_state_ptr.is_null() {
                None
            } else {
                Some(xkb::State::from_raw_ptr(xkb_state_ptr))
            }
        }
    }

    /// Get the repeat info for this keyboard.
    pub fn repeat_info(&self) -> RepeatInfo {
        unsafe {
            RepeatInfo { rate: (*self.keyboard).repeat_info.rate,
                         delay: (*self.keyboard).repeat_info.delay }
        }
    }

    /// Update the LED lights using the provided bitmap.
    ///
    /// 1 means one, 0 means off.
    pub fn update_led(&mut self, leds: KeyboardLed) {
        unsafe {
            wlr_keyboard_led_update(self.keyboard, leds.bits() as u32);
        }
    }

    /// Get the modifiers that are currently pressed on the keyboard.
    pub fn get_modifiers(&self) -> KeyboardModifier {
        unsafe {
            KeyboardModifier::from_bits_truncate(wlr_keyboard_get_modifiers(self.keyboard))
        }
    }

    /// Get the modifier masks for each group.
    pub fn get_modifier_masks(&self) -> ModifierMasks {
        unsafe {
            ModifierMasks { depressed: (*self.keyboard).modifiers.depressed,
                            latched: (*self.keyboard).modifiers.latched,
                            locked: (*self.keyboard).modifiers.locked,
                            group: (*self.keyboard).modifiers.group }
        }
    }

    /// Creates a weak reference to a `Keyboard`.
    ///
    /// # Panics
    /// If this `Keyboard` is a previously upgraded `KeyboardHandle`,
    /// then this function will panic.
    pub fn weak_reference(&self) -> KeyboardHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade previously upgraded KeyboardHandle!");
        KeyboardHandle { handle: Rc::downgrade(arc),
                         // NOTE Rationale for cloning:
                         // We can't use the keyboard handle unless the keyboard is alive,
                         // which means the device pointer is still alive.
                         device: unsafe { self.device.clone() },
                         keyboard: self.keyboard }
    }

    /// Manually set the lock used to determine if a double-borrow is
    /// occuring on this structure.
    ///
    /// # Panics
    /// Panics when trying to set the lock on an upgraded handle.
    pub(crate) unsafe fn set_lock(&self, val: bool) {
        self.liveliness.as_ref()
            .expect("Tried to set lock on borrowed Keyboard")
            .store(val, Ordering::Release);
    }
}

impl Drop for Keyboard {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    wlr_log!(L_DEBUG, "Dropped Keyboard {:p}", self.keyboard);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to Keyboard {:p}",
                                 weak_count,
                                 self.keyboard);
                    }
                }
            }
        }
    }
}

impl KeyboardHandle {
    /// Upgrades the keyboard handle to a reference to the backing `Keyboard`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbounded `Keyboard`
    /// which may live forever..
    /// But no keyboard lives forever and might be disconnected at any time.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<Keyboard> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::DoubleUpgrade)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let keyboard = Keyboard::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(keyboard)
            })
    }

    /// Run a function on the referenced Keyboard, if it still exists
    ///
    /// Returns the result of the function, if successful
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the output
    /// to a short lived scope of an anonymous function,
    /// this function ensures the Keyboard does not live longer
    /// than it exists.
    ///
    /// # Panics
    /// This function will panic if multiple mutable borrows are detected.
    /// This will happen if you call `upgrade` directly within this callback,
    /// or if you run this function within the another run to the same `Output`.
    ///
    /// So don't nest `run` calls and everything will be ok :).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<Option<R>>
        where F: FnOnce(&mut Keyboard) -> R
    {
        let mut keyboard = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| Some(runner(&mut keyboard))));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running keyboard callback, mutable \
                                                    lock was false for: {:?}",
                                                   keyboard);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    /// Gets the wlr_input_device associated with this KeyboardHandle
    pub fn input_device(&self) -> &InputDevice {
        &self.device
    }

    /// Gets the wlr_keyboard associated with this KeyboardHandle.
    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_keyboard {
        self.keyboard
    }
}

bitflags! {
    pub struct KeyboardLed: u32 {
        const WLR_LED_NUM_LOCK = wlr_keyboard_led::WLR_LED_NUM_LOCK as u32;
        const WLR_LED_CAPS_LOCK = wlr_keyboard_led::WLR_LED_CAPS_LOCK as u32;
        const WLR_LED_SCROLL_LOCK = wlr_keyboard_led::WLR_LED_SCROLL_LOCK as u32;
    }
}

bitflags! {
    pub struct KeyboardModifier: u32 {
        const WLR_MODIFIER_SHIFT = wlr_keyboard_modifier::WLR_MODIFIER_SHIFT as u32;
        const WLR_MODIFIER_CAPS = wlr_keyboard_modifier::WLR_MODIFIER_CAPS as u32;
        const WLR_MODIFIER_CTRL = wlr_keyboard_modifier::WLR_MODIFIER_CTRL as u32;
        const WLR_MODIFIER_ALT = wlr_keyboard_modifier::WLR_MODIFIER_ALT as u32;
        const WLR_MODIFIER_MOD2 = wlr_keyboard_modifier::WLR_MODIFIER_MOD2 as u32;
        const WLR_MODIFIER_MOD3 = wlr_keyboard_modifier::WLR_MODIFIER_MOD3 as u32;
        const WLR_MODIFIER_LOGO = wlr_keyboard_modifier::WLR_MODIFIER_LOGO as u32;
        const WLR_MODIFIER_MOD5 = wlr_keyboard_modifier::WLR_MODIFIER_MOD5 as u32;
    }
}

impl fmt::Display for KeyboardModifier {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let mod_vec = vec![("Shift", KeyboardModifier::WLR_MODIFIER_SHIFT),
                           ("Caps lock", KeyboardModifier::WLR_MODIFIER_CAPS),
                           ("Ctrl", KeyboardModifier::WLR_MODIFIER_CTRL),
                           ("Alt", KeyboardModifier::WLR_MODIFIER_ALT),
                           ("Mod2", KeyboardModifier::WLR_MODIFIER_MOD2),
                           ("Mod3", KeyboardModifier::WLR_MODIFIER_MOD3),
                           ("Logo", KeyboardModifier::WLR_MODIFIER_LOGO),
                           ("Mod5", KeyboardModifier::WLR_MODIFIER_MOD5)];

        let mods: Vec<&str> = mod_vec.into_iter()
                                     .filter(|&(_, flag)| self.contains(flag))
                                     .map(|(st, _)| st)
                                     .collect();

        write!(formatter, "{:?}", mods)
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub};
use typenum::uint::{Unsigned, UInt};
use typenum::bit::Bit;
use typenum::consts::{U0, U1};

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

/// Trait identifying a type as representing a tensor variance. It is implemented for
/// `CovariantIndex`, `ContravariantIndex` and tuples (Variance, Index).
pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

/// Trait identifying a type as representing a tensor index. It is implemented
/// for `CovariantIndex` and `ContravariantIndex`.
pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

/// Type representing a contravariant (upper) tensor index.
pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

/// Type representing a covariant (lower) tensor index.
pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

impl<T, U> Variance for (T, U)
    where U: Variance,
          <U::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          T: TensorIndex
{
    type Rank = <U::Rank as Add<U1>>::Output;

    fn variance() -> Vec<IndexType> {
        let mut result = vec![T::index_type()];
        result.append(&mut U::variance());
        result
    }
}

/// Operator trait used for concatenating two variances.
pub trait Concat<T> {
    type Output;
}

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: TensorIndex,
          V: TensorIndex,
          U: Variance + Concat<V>,
          <U as Concat<V>>::Output: Variance
{
    type Output = (T, <U as Concat<V>>::Output);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex,
          U: TensorIndex,
          V: Variance
{
    type Output = (T, (U, V));
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: TensorIndex,
          U: Variance + Concat<(V, W)>,
          V: TensorIndex,
          W: Variance
{
    type Output = (T, <U as Concat<(V, W)>>::Output);
}

/// Indexing operator trait: Output is equal to the index type at the given position
///
/// Warning: Indices are numbered starting from 0!
pub trait Index<T: Unsigned> {
    type Output;
}

impl Index<U0> for CovariantIndex {
    type Output = CovariantIndex;
}

impl Index<U0> for ContravariantIndex {
    type Output = ContravariantIndex;
}

impl<T, V, U, B> Index<UInt<U, B>> for (V, T)
    where V: TensorIndex,
          U: Unsigned,
          B: Bit,
          UInt<U, B>: Sub<U1>,
          <UInt<U, B> as Sub<U1>>::Output: Unsigned,
          T: Variance + Index<<UInt<U, B> as Sub<U1>>::Output>
{
    type Output = <T as Index<<UInt<U, B> as Sub<U1>>::Output>>::Output;
}

impl<T, V> Index<U0> for (V, T)
    where V: TensorIndex,
          T: Variance
{
    type Output = V;
}

#[cfg(test)]
mod test {
    use super::*;
    use typenum::consts::{U0, U1, U2};

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<<CovariantIndex as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(CovariantIndex, CovariantIndex) as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<CovariantIndex as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(ContravariantIndex, CovariantIndex) as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_index() {
        assert_eq!(<CovariantIndex as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U1>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U0>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U2>>::Output::index_type(),
            IndexType::Covariant);
    }
}<commit_msg>A small fix to the documentation<commit_after>use std::ops::{Add, Sub};
use typenum::uint::{Unsigned, UInt};
use typenum::bit::Bit;
use typenum::consts::{U0, U1};

/// This enum serves to represent the type of a tensor. A tensor can have any number of indices, and each one can be either
/// covariant (a lower index), or contravariant (an upper index). For example, a vector is a tensor with only one contravariant index.
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum IndexType {
    Covariant,
    Contravariant,
}

/// Trait identifying a type as representing a tensor variance. It is implemented for
/// `CovariantIndex`, `ContravariantIndex` and tuples (Index, Variance).
pub trait Variance {
    type Rank: Unsigned + Add<U1>;
    fn rank() -> usize {
        Self::Rank::to_usize()
    }
    fn variance() -> Vec<IndexType>;
}

/// Trait identifying a type as representing a tensor index. It is implemented
/// for `CovariantIndex` and `ContravariantIndex`.
pub trait TensorIndex: Variance {
    fn index_type() -> IndexType;
}

/// Type representing a contravariant (upper) tensor index.
pub struct ContravariantIndex;
impl TensorIndex for ContravariantIndex {
    fn index_type() -> IndexType {
        IndexType::Contravariant
    }
}

/// Type representing a covariant (lower) tensor index.
pub struct CovariantIndex;
impl TensorIndex for CovariantIndex {
    fn index_type() -> IndexType {
        IndexType::Covariant
    }
}

impl Variance for ContravariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Contravariant]
    }
}

impl Variance for CovariantIndex {
    type Rank = U1;
    fn variance() -> Vec<IndexType> {
        vec![IndexType::Covariant]
    }
}

impl<T, U> Variance for (T, U)
    where U: Variance,
          <U::Rank as Add<U1>>::Output: Unsigned + Add<U1>,
          T: TensorIndex
{
    type Rank = <U::Rank as Add<U1>>::Output;

    fn variance() -> Vec<IndexType> {
        let mut result = vec![T::index_type()];
        result.append(&mut U::variance());
        result
    }
}

/// Operator trait used for concatenating two variances.
pub trait Concat<T> {
    type Output;
}

impl<T, U> Concat<U> for T
    where T: TensorIndex,
          U: TensorIndex
{
    type Output = (T, U);
}

impl<T, U, V> Concat<V> for (T, U)
    where T: TensorIndex,
          V: TensorIndex,
          U: Variance + Concat<V>,
          <U as Concat<V>>::Output: Variance
{
    type Output = (T, <U as Concat<V>>::Output);
}

impl<T, U, V> Concat<(U, V)> for T
    where T: TensorIndex,
          U: TensorIndex,
          V: Variance
{
    type Output = (T, (U, V));
}

impl<T, U, V, W> Concat<(V, W)> for (T, U)
    where T: TensorIndex,
          U: Variance + Concat<(V, W)>,
          V: TensorIndex,
          W: Variance
{
    type Output = (T, <U as Concat<(V, W)>>::Output);
}

/// Indexing operator trait: Output is equal to the index type at the given position
///
/// Warning: Indices are numbered starting from 0!
pub trait Index<T: Unsigned> {
    type Output;
}

impl Index<U0> for CovariantIndex {
    type Output = CovariantIndex;
}

impl Index<U0> for ContravariantIndex {
    type Output = ContravariantIndex;
}

impl<T, V, U, B> Index<UInt<U, B>> for (V, T)
    where V: TensorIndex,
          U: Unsigned,
          B: Bit,
          UInt<U, B>: Sub<U1>,
          <UInt<U, B> as Sub<U1>>::Output: Unsigned,
          T: Variance + Index<<UInt<U, B> as Sub<U1>>::Output>
{
    type Output = <T as Index<<UInt<U, B> as Sub<U1>>::Output>>::Output;
}

impl<T, V> Index<U0> for (V, T)
    where V: TensorIndex,
          T: Variance
{
    type Output = V;
}

#[cfg(test)]
mod test {
    use super::*;
    use typenum::consts::{U0, U1, U2};

    #[test]
    fn test_variance() {
        assert_eq!(<(CovariantIndex, ContravariantIndex) as Variance>::variance(), vec![IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_variance_concat() {
        assert_eq!(<<CovariantIndex as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(CovariantIndex, CovariantIndex) as Concat<ContravariantIndex>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<CovariantIndex as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);

        assert_eq!(<<(ContravariantIndex, CovariantIndex) as Concat<(CovariantIndex, ContravariantIndex)>>::Output as Variance>::variance(),
            vec![IndexType::Contravariant, IndexType::Covariant, IndexType::Covariant, IndexType::Contravariant]);
    }

    #[test]
    fn test_index() {
        assert_eq!(<CovariantIndex as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U0>>::Output::index_type(),
            IndexType::Covariant);

        assert_eq!(<(CovariantIndex, ContravariantIndex) as Index<U1>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U0>>::Output::index_type(),
            IndexType::Contravariant);

        assert_eq!(<(ContravariantIndex, (CovariantIndex, CovariantIndex)) as Index<U2>>::Output::index_type(),
            IndexType::Covariant);
    }
}<|endoftext|>


<commit_before>// ignore-windows: TODO clock shims are not implemented on Windows
// compile-flags: -Zmiri-disable-isolation

use std::time::{SystemTime, Instant};

fn main() {
    let now1 = SystemTime::now();
    // Do some work to make time pass.
    for _ in 0..10 { drop(vec![42]); }
    let now2 = SystemTime::now();
    assert!(now2 > now1);

    let now1 = Instant::now();
    // Do some work to make time pass.
    for _ in 0..10 { drop(vec![42]); }
    let now2 = Instant::now();
    assert!(now2 > now1);
}
<commit_msg>test subtracting SystemTime and Instant<commit_after>// ignore-windows: TODO clock shims are not implemented on Windows
// compile-flags: -Zmiri-disable-isolation

use std::time::{SystemTime, Instant};

fn main() {
    let now1 = SystemTime::now();
    // Do some work to make time pass.
    for _ in 0..10 { drop(vec![42]); }
    let now2 = SystemTime::now();
    assert!(now2 > now1);
    let diff = now2.duration_since(now1).unwrap();
    assert!(diff.as_micros() > 0);
    assert_eq!(now1 + diff, now2);
    assert_eq!(now2 - diff, now1);

    let now1 = Instant::now();
    // Do some work to make time pass.
    for _ in 0..10 { drop(vec![42]); }
    let now2 = Instant::now();
    assert!(now2 > now1);

    #[cfg(target_os = "linux")]
    {
        let diff = now2.duration_since(now1);
        assert!(diff.as_micros() > 0);
        assert_eq!(now1 + diff, now2);
        assert_eq!(now2 - diff, now1);
    }
}
<|endoftext|>


<commit_before>//! This is a simle crate module

use std::io::prelude::*;
use std::io::BufReader;
use std::io::Error;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::collections;
use std::env;

use toml;
use rustc_serialize::json::Json;
use rustc_serialize::json::ParserError;

use super::{DocBuilder, DocBuilderError, copy_files, command_result};

/// Really simple crate model
#[derive(Debug)]
pub struct Crate {
    /// Name of crate
    pub name: String,
    /// Versions of crate
    pub versions: Vec<String>,
}

#[derive(Debug)]
pub enum CrateOpenError {
    FileNotFound,
    ParseError(ParserError),
    IoError(Error),
    NotObject,
    NameNotFound,
    VersNotFound,
}

impl Crate {
    /// Returns a new Crate
    pub fn new(name: String, versions: Vec<String>) -> Crate {
        Crate {
            name: name,
            versions: versions,
        }
    }

    pub fn from_cargo_index_file(path: PathBuf) -> Result<Crate, CrateOpenError> {

        let mut file = match fs::File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(_) => return Err(CrateOpenError::FileNotFound),
        };

        let mut line = String::new();

        let mut name = String::new();
        let mut versions = Vec::new();

        while try!(file.read_line(&mut line).map_err(CrateOpenError::IoError)) > 0 {

            let (cname, vers) = match Crate::parse_cargo_index_line(&line) {
                Ok((c, v)) => (c, v),
                Err(e) => return Err(e)
            };

            name = cname;
            versions.push(vers);

            line.clear();
        }

        versions.reverse();

        Ok(Crate {
            name: name,
            versions: versions
        })
    }

    /// Create Crate from crate name and try to find it in crates.io-index
    /// to load versions
    pub fn from_cargo_index_path(name: &str, path: &PathBuf) -> Result<Crate, CrateOpenError> {

        if !path.is_dir() {
            return Err(CrateOpenError::FileNotFound);
        }

        for file in try!(path.read_dir().map_err(CrateOpenError::IoError)) {

            let file = try!(file.map_err(CrateOpenError::IoError));

            let path = file.path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                if let Ok(c) = Crate::from_cargo_index_path(&name, &path) {
                    return Ok(c);
                }
            } else if file.file_name().into_string().unwrap() == name {
                return Crate::from_cargo_index_file(path);
            }

        }

        Err(CrateOpenError::FileNotFound)
    }

    fn parse_cargo_index_line(line: &String) -> Result<(String, String), CrateOpenError> {
        let data = try!(Json::from_str(line.trim()).map_err(CrateOpenError::ParseError));
        let obj = try!(data.as_object().ok_or(CrateOpenError::NotObject));

        let crate_name = try!(obj.get("name")
                              .and_then(|n| n.as_string())
                              .ok_or(CrateOpenError::NameNotFound));

        let vers = try!(obj.get("vers")
                        .and_then(|n| n.as_string())
                        .ok_or(CrateOpenError::VersNotFound));

        Ok((String::from(crate_name), String::from(vers)))
    }

    pub fn get_version_index(&self, requested_version: &str) -> Option<usize> {
        for i in 0..self.versions.len() {
            if self.versions[i] == requested_version {
                return Some(i);
            }
        }
        None
    }

    pub fn version_starts_with(&self, version: &str) -> Option<usize> {
        // if version is "*" return latest version index which is 0
        if version == "*" {
            return Some(0);
        }
        for i in 0..self.versions.len() {
            if self.versions[i].starts_with(&version) {
                return Some(i)
            }
        }
        None
    }

    pub fn canonical_name(&self, version_index: usize) -> String {
        format!("{}-{}", self.name, self.versions[version_index])
    }

    pub fn extract_crate(&self, version_index: usize) -> Result<String, String> {
        let crate_name = format!("{}.crate", self.canonical_name(version_index));
        command_result(Command::new("tar")
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

    /// Downloads crate
    pub fn download_crate(&self, version_index: usize) -> Result<String, String> {
        // By default crates.io is using:
        // https://crates.io/api/v1/crates/$crate/$version/download
        // But I believe this url is increasing download count and this program is
        // downloading alot during development. I am using redirected url.
        let url = format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                          self.name,
                          self.name,
                          self.versions[version_index]);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf, docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path)
                                     .map_err(DocBuilderError::LocalDependencyIoError));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content)
             .map_err(DocBuilderError::LocalDependencyIoError));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| self.get_local_dependencies(dependencies_table, docbuilder))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir))
            .unwrap_or(Ok(()))
    }

    /// Get's local_dependencies from dependencies_table
    fn get_local_dependencies(&self,
                              dependencies_table: &collections::BTreeMap<String, toml::Value>,
                              docbuilder: &DocBuilder) -> Option<Vec<(Crate, usize, String)>>  {

        let mut local_dependencies: Vec<(Crate, usize, String)> = Vec::new();

        for key in dependencies_table.keys() {

            dependencies_table.get(key)
                .and_then(|key_val| key_val.as_table())
                .map(|key_table| {
                    key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                        key_table.get("version").and_then(|p| p.as_str())
                            .map(|version| {
                                // TODO: This kinda became a mess
                                //       I wonder if can use more and_then's...
                                if let Ok(dep_crate) = Crate::from_cargo_index_path(&key,
                                                            &docbuilder.crates_io_index_path) {
                                    if let Some(version_index) =
                                        dep_crate.version_starts_with(version) {
                                        local_dependencies.push((dep_crate,
                                                                 version_index,
                                                                 path.to_string()));
                                    }
                                }
                            });
                    });
                });

        }
        Some(local_dependencies)
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(Crate, usize, String)>,
                                 root_dir: &PathBuf) -> Result<(), DocBuilderError> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;
            let version_index = local_dependency.1;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.2);

            // make sure path exists
            if !path.exists() {
                try!(fs::create_dir_all(&path).map_err(DocBuilderError::LocalDependencyIoError));
            }

            try!(crte.download_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyDownloadError));
            try!(crte.extract_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyExtractCrateError));

            let crte_download_dir = PathBuf::from(format!("{}-{}",
                                                          crte.name,
                                                          crte.versions[version_index]));

            if !crte_download_dir.exists() {
                return Err(DocBuilderError::LocalDependencyDownloadDirNotExist);
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            try!(copy_files(&crte_download_dir, &path));

            // Remove download_dir
            try!(fs::remove_dir_all(&crte_download_dir)
                 .map_err(DocBuilderError::LocalDependencyIoError));

            try!(crte.remove_crate_file(version_index));
        }

        Ok(())
    }

    fn remove_build_dir_for_crate(&self,
                                  version_index: usize) -> Result<(), DocBuilderError> {
        let path = PathBuf::from(self.canonical_name(version_index));

        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(DocBuilderError::RemoveBuildDir));
        }

        Ok(())
    }

    pub fn build_crate_doc(&self,
                           version_index: usize,
                           docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let package_root = PathBuf::from(self.canonical_name(version_index));

        info!("Building documentation for {}-{}", self.name, self.versions[version_index]);

        // removing old build directory
        try!(self.remove_build_dir_for_crate(version_index));

        // Download crate
        // FIXME: Need to capture failed command outputs
        info!("Downloading crate\n{}",
              try!(self.download_crate(version_index)
                   .map_err(DocBuilderError::DownloadCrateError)));

        // Extract crate
        info!("Extracting crate\n{}",
              try!(self.extract_crate(version_index)
                   .map_err(DocBuilderError::ExtractCrateError)));

        info!("Checking local dependencies");
        try!(self.download_dependencies(&package_root, &docbuilder));

        // build docs
        info!("Building documentation");
        let (status, message) = match self.build_doc(version_index) {
            Ok(m) => (true, m),
            Err(m) => (false, m),
        };
        info!("cargo doc --no-deps --verbose\n{}", message);

        if status {
            Ok(())
        } else {
            Err(DocBuilderError::FailedToBuildCrate)
        }
    }

    fn build_doc(&self, version_index: usize) -> Result<String, String> {
        let cwd = env::current_dir().unwrap();
        let mut target = PathBuf::from(&cwd);
        target.push(self.canonical_name(version_index));
        env::set_current_dir(target).unwrap();
        let res = command_result(Command::new("cargo")
                                 .arg("doc")
                                 .arg("--no-deps")
                                 .arg("--verbose")
                                 .output()
                                 .unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    }

    pub fn remove_crate_file(&self,
                             version_index: usize) -> Result<(), DocBuilderError>{
        let path = PathBuf::from(format!("{}.crate", self.canonical_name(version_index)));

        if path.exists() && path.is_file() {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveCrateFile));
        }

        Ok(())
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use std::env;
    use std::path::PathBuf;
    use std::fs;

    #[test]
    fn test_get_vesion_index() {
        let crte = Crate::new("cratesfyi".to_string(),
                              vec!["0.1.0".to_string(), "0.1.1".to_string()]);
        assert_eq!(crte.get_version_index("0.1.0"), Some(0));
        assert_eq!(crte.get_version_index("0.1.1"), Some(1));
        assert_eq!(crte.get_version_index("0.1.2"), None);
    }

    // Rest of the tests only works if crates.io-index is exists in:
    // ../cratesfyi-prefix/crates.io-index

    #[test]
    #[ignore]
    fn test_from_cargo_index_path() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert_eq!(crte.name, "rand");
        assert!(crte.versions.len() > 0);
    }

    #[test]
    #[ignore]
    fn test_version_starts_with() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert!(crte.version_starts_with("0.1").is_some());
        assert!(crte.version_starts_with("*").is_some());
        assert!(crte.version_starts_with("999.099.99").is_none());
    }

    #[test]
    fn test_download_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.13".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        // remove downloaded file
        fs::remove_file(format!("{}.crate", crte.canonical_name(0))).unwrap();
    }

    #[test]
    fn test_extract_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.12".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.extract_crate(0).is_ok());

        let path = PathBuf::from(crte.canonical_name(0));
        assert!(path.exists());
        fs::remove_dir_all(crte.canonical_name(0)).unwrap();
    }

    #[test]
    fn test_remove_crate_file() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.11".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.remove_crate_file(0).is_ok());
    }

}
<commit_msg>Expand doc a little bit<commit_after>//! This is a simle crate module

use std::io::prelude::*;
use std::io::BufReader;
use std::io::Error;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::collections;
use std::env;

use toml;
use rustc_serialize::json::Json;
use rustc_serialize::json::ParserError;

use super::{DocBuilder, DocBuilderError, copy_files, command_result};

/// Really simple crate model
#[derive(Debug)]
pub struct Crate {
    /// Name of crate
    pub name: String,
    /// Versions of crate
    pub versions: Vec<String>,
}

#[derive(Debug)]
pub enum CrateOpenError {
    FileNotFound,
    ParseError(ParserError),
    IoError(Error),
    NotObject,
    NameNotFound,
    VersNotFound,
}

impl Crate {
    /// Returns a new Crate
    pub fn new(name: String, versions: Vec<String>) -> Crate {
        Crate {
            name: name,
            versions: versions,
        }
    }

    /// Creates a new crate from crates.io-index path file
    pub fn from_cargo_index_file(path: PathBuf) -> Result<Crate, CrateOpenError> {

        let mut file = match fs::File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(_) => return Err(CrateOpenError::FileNotFound),
        };

        let mut line = String::new();

        let mut name = String::new();
        let mut versions = Vec::new();

        while try!(file.read_line(&mut line).map_err(CrateOpenError::IoError)) > 0 {

            let (cname, vers) = match Crate::parse_cargo_index_line(&line) {
                Ok((c, v)) => (c, v),
                Err(e) => return Err(e)
            };

            name = cname;
            versions.push(vers);

            line.clear();
        }

        versions.reverse();

        Ok(Crate {
            name: name,
            versions: versions
        })
    }

    /// Create Crate from crate name and try to find it in crates.io-index
    /// to load versions
    pub fn from_cargo_index_path(name: &str, path: &PathBuf) -> Result<Crate, CrateOpenError> {

        if !path.is_dir() {
            return Err(CrateOpenError::FileNotFound);
        }

        for file in try!(path.read_dir().map_err(CrateOpenError::IoError)) {

            let file = try!(file.map_err(CrateOpenError::IoError));

            let path = file.path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                if let Ok(c) = Crate::from_cargo_index_path(&name, &path) {
                    return Ok(c);
                }
            } else if file.file_name().into_string().unwrap() == name {
                return Crate::from_cargo_index_file(path);
            }

        }

        Err(CrateOpenError::FileNotFound)
    }

    fn parse_cargo_index_line(line: &String) -> Result<(String, String), CrateOpenError> {
        let data = try!(Json::from_str(line.trim()).map_err(CrateOpenError::ParseError));
        let obj = try!(data.as_object().ok_or(CrateOpenError::NotObject));

        let crate_name = try!(obj.get("name")
                              .and_then(|n| n.as_string())
                              .ok_or(CrateOpenError::NameNotFound));

        let vers = try!(obj.get("vers")
                        .and_then(|n| n.as_string())
                        .ok_or(CrateOpenError::VersNotFound));

        Ok((String::from(crate_name), String::from(vers)))
    }

    /// Returns index of requested_version
    pub fn get_version_index(&self, requested_version: &str) -> Option<usize> {
        for i in 0..self.versions.len() {
            if self.versions[i] == requested_version {
                return Some(i);
            }
        }
        None
    }

    /// Returns index of version if it starts with it
    pub fn version_starts_with(&self, version: &str) -> Option<usize> {
        // if version is "*" return latest version index which is 0
        if version == "*" {
            return Some(0);
        }
        for i in 0..self.versions.len() {
            if self.versions[i].starts_with(&version) {
                return Some(i)
            }
        }
        None
    }

    /// Returns canonical name of crate, i.e: "rand-0.1.13"
    pub fn canonical_name(&self, version_index: usize) -> String {
        format!("{}-{}", self.name, self.versions[version_index])
    }

    /// Extracts crate into CWD
    pub fn extract_crate(&self, version_index: usize) -> Result<String, String> {
        let crate_name = format!("{}.crate", self.canonical_name(version_index));
        command_result(Command::new("tar")
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

    /// Downloads crate into CWD
    pub fn download_crate(&self, version_index: usize) -> Result<String, String> {
        // By default crates.io is using:
        // https://crates.io/api/v1/crates/$crate/$version/download
        // But I believe this url is increasing download count and this program is
        // downloading alot during development. I am using redirected url.
        let url = format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                          self.name,
                          self.name,
                          self.versions[version_index]);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf, docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path)
                                     .map_err(DocBuilderError::LocalDependencyIoError));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content)
             .map_err(DocBuilderError::LocalDependencyIoError));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| self.get_local_dependencies(dependencies_table, docbuilder))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir))
            .unwrap_or(Ok(()))
    }

    /// Get's local_dependencies from dependencies_table
    fn get_local_dependencies(&self,
                              dependencies_table: &collections::BTreeMap<String, toml::Value>,
                              docbuilder: &DocBuilder) -> Option<Vec<(Crate, usize, String)>>  {

        let mut local_dependencies: Vec<(Crate, usize, String)> = Vec::new();

        for key in dependencies_table.keys() {

            dependencies_table.get(key)
                .and_then(|key_val| key_val.as_table())
                .map(|key_table| {
                    key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                        key_table.get("version").and_then(|p| p.as_str())
                            .map(|version| {
                                // TODO: This kinda became a mess
                                //       I wonder if can use more and_then's...
                                if let Ok(dep_crate) = Crate::from_cargo_index_path(&key,
                                                            &docbuilder.crates_io_index_path) {
                                    if let Some(version_index) =
                                        dep_crate.version_starts_with(version) {
                                        local_dependencies.push((dep_crate,
                                                                 version_index,
                                                                 path.to_string()));
                                    }
                                }
                            });
                    });
                });

        }
        Some(local_dependencies)
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(Crate, usize, String)>,
                                 root_dir: &PathBuf) -> Result<(), DocBuilderError> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;
            let version_index = local_dependency.1;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.2);

            // make sure path exists
            if !path.exists() {
                try!(fs::create_dir_all(&path).map_err(DocBuilderError::LocalDependencyIoError));
            }

            try!(crte.download_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyDownloadError));
            try!(crte.extract_crate(version_index)
                 .map_err(DocBuilderError::LocalDependencyExtractCrateError));

            let crte_download_dir = PathBuf::from(format!("{}-{}",
                                                          crte.name,
                                                          crte.versions[version_index]));

            if !crte_download_dir.exists() {
                return Err(DocBuilderError::LocalDependencyDownloadDirNotExist);
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            try!(copy_files(&crte_download_dir, &path));

            // Remove download_dir
            try!(fs::remove_dir_all(&crte_download_dir)
                 .map_err(DocBuilderError::LocalDependencyIoError));

            try!(crte.remove_crate_file(version_index));
        }

        Ok(())
    }

    fn remove_build_dir_for_crate(&self,
                                  version_index: usize) -> Result<(), DocBuilderError> {
        let path = PathBuf::from(self.canonical_name(version_index));

        if path.exists() && path.is_dir() {
            try!(fs::remove_dir_all(&path).map_err(DocBuilderError::RemoveBuildDir));
        }

        Ok(())
    }

    /// Builds crate documentation
    pub fn build_crate_doc(&self,
                           version_index: usize,
                           docbuilder: &DocBuilder) -> Result<(), DocBuilderError> {

        let package_root = PathBuf::from(self.canonical_name(version_index));

        info!("Building documentation for {}-{}", self.name, self.versions[version_index]);

        // removing old build directory
        try!(self.remove_build_dir_for_crate(version_index));

        // Download crate
        // FIXME: Need to capture failed command outputs
        info!("Downloading crate\n{}",
              try!(self.download_crate(version_index)
                   .map_err(DocBuilderError::DownloadCrateError)));

        // Extract crate
        info!("Extracting crate\n{}",
              try!(self.extract_crate(version_index)
                   .map_err(DocBuilderError::ExtractCrateError)));

        info!("Checking local dependencies");
        try!(self.download_dependencies(&package_root, &docbuilder));

        // build docs
        info!("Building documentation");
        let (status, message) = match self.build_doc(version_index) {
            Ok(m) => (true, m),
            Err(m) => (false, m),
        };
        info!("cargo doc --no-deps --verbose\n{}", message);

        if status {
            Ok(())
        } else {
            Err(DocBuilderError::FailedToBuildCrate)
        }
    }

    fn build_doc(&self, version_index: usize) -> Result<String, String> {
        let cwd = env::current_dir().unwrap();
        let mut target = PathBuf::from(&cwd);
        target.push(self.canonical_name(version_index));
        env::set_current_dir(target).unwrap();
        let res = command_result(Command::new("cargo")
                                 .arg("doc")
                                 .arg("--no-deps")
                                 .arg("--verbose")
                                 .output()
                                 .unwrap());
        env::set_current_dir(cwd).unwrap();
        res
    }

    /// Removes crate file if it's exists in CWD
    pub fn remove_crate_file(&self,
                             version_index: usize) -> Result<(), DocBuilderError>{
        let path = PathBuf::from(format!("{}.crate", self.canonical_name(version_index)));

        if path.exists() && path.is_file() {
            try!(fs::remove_file(path).map_err(DocBuilderError::RemoveCrateFile));
        }

        Ok(())
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use std::env;
    use std::path::PathBuf;
    use std::fs;

    #[test]
    fn test_get_vesion_index() {
        let crte = Crate::new("cratesfyi".to_string(),
                              vec!["0.1.0".to_string(), "0.1.1".to_string()]);
        assert_eq!(crte.get_version_index("0.1.0"), Some(0));
        assert_eq!(crte.get_version_index("0.1.1"), Some(1));
        assert_eq!(crte.get_version_index("0.1.2"), None);
    }

    // Rest of the tests only works if crates.io-index is exists in:
    // ../cratesfyi-prefix/crates.io-index

    #[test]
    #[ignore]
    fn test_from_cargo_index_path() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert_eq!(crte.name, "rand");
        assert!(crte.versions.len() > 0);
    }

    #[test]
    #[ignore]
    fn test_version_starts_with() {
        let mut path = PathBuf::from(env::current_dir().unwrap());
        path.push("../cratesfyi-prefix/crates.io-index");

        if !path.exists() {
            return;
        }

        let crte = Crate::from_cargo_index_path("rand", &path).unwrap();
        assert!(crte.version_starts_with("0.1").is_some());
        assert!(crte.version_starts_with("*").is_some());
        assert!(crte.version_starts_with("999.099.99").is_none());
    }

    #[test]
    fn test_download_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.13".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        // remove downloaded file
        fs::remove_file(format!("{}.crate", crte.canonical_name(0))).unwrap();
    }

    #[test]
    fn test_extract_crate() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.12".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.extract_crate(0).is_ok());

        let path = PathBuf::from(crte.canonical_name(0));
        assert!(path.exists());
        fs::remove_dir_all(crte.canonical_name(0)).unwrap();
    }

    #[test]
    fn test_remove_crate_file() {
        let crte = Crate::new("rand".to_string(),
                              vec!["0.3.11".to_string()]);
        assert!(crte.download_crate(0).is_ok());
        assert!(crte.remove_crate_file(0).is_ok());
    }

}
<|endoftext|>