<commit_before>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory. Message storage and
//! retrieval are managed by a `Storage` object, which is configured for a single root directory.

use std::fs;
use std::path::{Path, PathBuf};
use std::result;

use glob::{glob, Paths, PatternError};

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
pub struct StorageIterator {
    paths: Option<Paths>,
}

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry;

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

impl StorageIterator {
    fn new(storage: &Storage) -> StorageIterator {
        let paths = match storage.glob() {
            Ok(paths) => Some(paths),
            Err(_) => None,
        };
        StorageIterator {
            paths: paths,
        }
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator::new(self)
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage {
            root: root.as_ref().to_path_buf(),
        }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session().format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION)).to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    fn glob(&self) -> result::Result<Paths, PatternError> {
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        glob(&path_buf.to_str().unwrap())
    }
}

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

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<commit_msg>Add a storage iterator<commit_after>//! Manage SBD messages stored on the fileystem.
//!
//! Messages are stored in a directory hierarchy under a single root directory. Message storage and
//! retrieval are managed by a `Storage` object, which is configured for a single root directory.

use std::fs;
use std::path::{Path, PathBuf};
use std::result;

use glob::{glob, Paths, PatternError};

use super::{Message, Result};

const SBD_EXTENSION: &'static str = ".sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
pub struct Storage {
    root: PathBuf,
}

/// An interator over the messages in a `Storage`.
pub struct StorageIterator {
    paths: Option<Paths>,
}

/// The object yielded by a `StorageIterator`.
pub struct StorageEntry {
    pub message: Message,
    pub path_buf: PathBuf,
}

impl Iterator for StorageIterator {
    type Item = StorageEntry;

    fn next(&mut self) -> Option<Self::Item> {
        let mut paths = match self.paths {
            Some(ref mut paths) => paths,
            None => return None,
        };
        loop {
            let glob_result = match paths.next() {
                Some(result) => result,
                None => return None,
            };
            let path_buf = match glob_result {
                Ok(path_buf) => path_buf,
                Err(_) => continue,
            };
            match Message::from_path(&path_buf) {
                Ok(message) => return Some(StorageEntry {
                    message: message,
                    path_buf: path_buf,
                }),
                Err(_) => continue,
            };
        }
    }
}

impl StorageIterator {
    fn new(storage: &Storage) -> StorageIterator {
        let paths = match storage.glob() {
            Ok(paths) => Some(paths),
            Err(_) => None,
        };
        StorageIterator {
            paths: paths,
        }
    }
}

impl<'a> IntoIterator for &'a Storage {
    type Item = StorageEntry;
    type IntoIter = StorageIterator;

    fn into_iter(self) -> Self::IntoIter {
        StorageIterator::new(self)
    }
}

impl Storage {
    /// Creates a new storage manager for a given root directory.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// let storage = Storage::new("/var/iridium");
    /// ```
    pub fn new<P: AsRef<Path>>(root: P) -> Storage {
        Storage {
            root: root.as_ref().to_path_buf(),
        }
    }

    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::filesystem::Storage;
    /// use sbd::Message;
    /// let message: Message = Default::default();
    /// let storage = Storage::new("/var/iridium");
    /// storage.store(&message);
    /// ```
    pub fn store(&self, message: &Message) -> Result<PathBuf> {
        let mut path_buf = self.root.clone();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session().format(&format!("%y%m%d_%H%M%S{}", SBD_EXTENSION)).to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(path_buf)
    }

    fn glob(&self) -> result::Result<Paths, PatternError> {
        let mut path_buf = self.root.clone();
        path_buf.push("**");
        path_buf.push(format!("*{}", SBD_EXTENSION));
        glob(&path_buf.to_str().unwrap())
    }
}

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

    use tempdir::TempDir;

    use super::super::Message;

    #[test]
    fn new_storage() {
        Storage::new(TempDir::new("").unwrap().path());
    }

    #[test]
    fn store_message() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        let pathbuf = storage.store(&message).unwrap();
        Message::from_path(pathbuf).unwrap();
    }

    #[test]
    fn iterate_over_messages() {
        let storage = Storage::new(TempDir::new("").unwrap().path());
        let message: Message = Default::default();
        storage.store(&message).unwrap();
        let messages = storage.into_iter().collect::<Vec<StorageEntry>>();
        assert_eq!(1, messages.len());
    }
}
<|endoftext|>


<commit_before>use std::convert::From;
use std::error::Error;
use std::result;
use std::fmt;
use snap;
use lz4_compress as lz4;

type Result<T> = result::Result<T, CompressionError>;

pub const LZ4: &'static str = "lz4";
pub const SNAPPY: &'static str = "snappy";

/// It's an error which may occure during encoding or deconding
/// frame body. As there are only two types of compressors it
/// contains two related enum options.
#[derive(Debug)]
pub enum CompressionError {
    /// Snappy error.
    Snappy(Box<Error>),
    /// Lz4 error.
    Lz4(String),
}

impl fmt::Display for CompressionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &CompressionError::Snappy(ref err) => write!(f, "Snappy Error: {:?}", err),
            &CompressionError::Lz4(ref s) => write!(f, "Lz4 Error: {:?}", s),
        }
    }
}

impl Error for CompressionError {
    fn description(&self) -> &str {
        let desc = match self {
            &CompressionError::Snappy(ref err) => err.description(),
            &CompressionError::Lz4(ref s) => s.as_str(),
        };

        return desc;
    }
}

/// Compressor trait that defines functionality
/// which should be provided by typical compressor.
pub trait Compressor {
    /// Encodes given bytes and returns `Result` that contains either
    /// encoded data or an error which occures during the transformation.
    fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Encodes given encoded data and returns `Result` that contains either
    /// encoded bytes or an error which occures during the transformation.
    fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Returns a string which is a name of a compressor. This name should be
    /// exactly the same as one which returns a server in a response to
    /// `Options` request.
    fn into_string(&self) -> Option<String>;
}

/// Enum which represents a type of compression. Only non-startup frame's body can be compressen.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Compression {
    /// [lz4](https://code.google.com/p/lz4/) compression
    Lz4,
    /// [snappy](https://code.google.com/p/snappy/) compression
    Snappy,
    /// Non compression
    None,
}

impl Compression {
    /// It encodes `bytes` basing on type of `Compression`..
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///
    ///   let snappy_compression = Compression::Snappy;
    ///   let bytes = String::from("Hello World").into_bytes().to_vec();
    ///   let encoded = snappy_compression.encode(bytes.clone()).unwrap();
    ///   assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    ///
    /// ```
    pub fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::encode_lz4(bytes),
            &Compression::Snappy => Compression::encode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It decodes `bytes` basing on type of compression.
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///     let lz4_compression = Compression::Lz4;
    ///     let bytes = String::from("Hello World").into_bytes().to_vec();
    ///     let encoded = lz4_compression.encode(bytes.clone()).unwrap();
    ///     let len = encoded.len() as u8;
    ///     let mut input = vec![0, 0, 0, len];
    ///     input.extend_from_slice(encoded.as_slice());
    ///     assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    /// ```
    pub fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::decode_lz4(bytes),
            &Compression::Snappy => Compression::decode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It transforms compression method into a `&str`.
    pub fn as_str(&self) -> Option<&'static str> {
        return match self {
            &Compression::Lz4 => Some(LZ4),
            &Compression::Snappy => Some(SNAPPY),
            &Compression::None => None,
        };
    }

    fn encode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut encoder = snap::Encoder::new();
        return encoder.compress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn decode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut decoder = snap::Decoder::new();
        return decoder.decompress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn encode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        return Ok(lz4::compress(bytes.as_slice()));
    }

    fn decode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        // skip first 4 bytes in accordance to
        // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L805
        return lz4::decompress(&bytes[4..])
            .map_err(|err| CompressionError::Lz4(err.description().to_string()));
    }
}

impl From<String> for Compression {
    /// It converts `String` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_string: String) -> Compression {
        return Compression::from(compression_string.as_str());
    }
}

impl<'a> From<&'a str> for Compression {
    /// It converts `str` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_str: &'a str) -> Compression {
        return match compression_str {
            LZ4 => Compression::Lz4,
            SNAPPY => Compression::Snappy,
            _ => Compression::None,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_compression_from_str() {
        let lz4 = "lz4";
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy";
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x";
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_from_string() {
        let lz4 = "lz4".to_string();
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy".to_string();
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x".to_string();
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_encode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = snappy_compression.encode(bytes.clone()).unwrap();
        assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_lz4() {
        let snappy_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_lz4() {
        let lz4_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let len = encoded.len() as u8;
        let mut input = vec![0, 0, 0, len];
        input.extend_from_slice(encoded.as_slice());
        assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        none_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = none_compression.encode(bytes.clone()).unwrap();
        assert_eq!(none_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_snappy_with_non_utf8() {
        let lz4_compression = Compression::Lz4;
        let bytes: Vec<u8> = vec![0x7f,0x7f,0x7f,0x7f,0x7f];
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let decode = lz4_compression.decode(encoded);
        assert_eq!(decode.is_err(),true);
    }

}
<commit_msg>added test case for error<commit_after>use std::convert::From;
use std::error::Error;
use std::result;
use std::fmt;
use snap;
use lz4_compress as lz4;

type Result<T> = result::Result<T, CompressionError>;

pub const LZ4: &'static str = "lz4";
pub const SNAPPY: &'static str = "snappy";

/// It's an error which may occure during encoding or deconding
/// frame body. As there are only two types of compressors it
/// contains two related enum options.
#[derive(Debug)]
pub enum CompressionError {
    /// Snappy error.
    Snappy(Box<Error>),
    /// Lz4 error.
    Lz4(String),
}

impl fmt::Display for CompressionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &CompressionError::Snappy(ref err) => write!(f, "Snappy Error: {:?}", err),
            &CompressionError::Lz4(ref s) => write!(f, "Lz4 Error: {:?}", s),
        }
    }
}

impl Error for CompressionError {
    fn description(&self) -> &str {
        let desc = match self {
            &CompressionError::Snappy(ref err) => err.description(),
            &CompressionError::Lz4(ref s) => s.as_str(),
        };

        return desc;
    }
}

/// Compressor trait that defines functionality
/// which should be provided by typical compressor.
pub trait Compressor {
    /// Encodes given bytes and returns `Result` that contains either
    /// encoded data or an error which occures during the transformation.
    fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Encodes given encoded data and returns `Result` that contains either
    /// encoded bytes or an error which occures during the transformation.
    fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>>;
    /// Returns a string which is a name of a compressor. This name should be
    /// exactly the same as one which returns a server in a response to
    /// `Options` request.
    fn into_string(&self) -> Option<String>;
}

/// Enum which represents a type of compression. Only non-startup frame's body can be compressen.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Compression {
    /// [lz4](https://code.google.com/p/lz4/) compression
    Lz4,
    /// [snappy](https://code.google.com/p/snappy/) compression
    Snappy,
    /// Non compression
    None,
}

impl Compression {
    /// It encodes `bytes` basing on type of `Compression`..
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///
    ///   let snappy_compression = Compression::Snappy;
    ///   let bytes = String::from("Hello World").into_bytes().to_vec();
    ///   let encoded = snappy_compression.encode(bytes.clone()).unwrap();
    ///   assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    ///
    /// ```
    pub fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::encode_lz4(bytes),
            &Compression::Snappy => Compression::encode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It decodes `bytes` basing on type of compression.
    ///
    /// # Examples
    ///
    /// ```
    ///    use cdrs::compression::Compression;
    ///     let lz4_compression = Compression::Lz4;
    ///     let bytes = String::from("Hello World").into_bytes().to_vec();
    ///     let encoded = lz4_compression.encode(bytes.clone()).unwrap();
    ///     let len = encoded.len() as u8;
    ///     let mut input = vec![0, 0, 0, len];
    ///     input.extend_from_slice(encoded.as_slice());
    ///     assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    /// ```
    pub fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::decode_lz4(bytes),
            &Compression::Snappy => Compression::decode_snappy(bytes),
            &Compression::None => Ok(bytes),
        };
    }

    /// It transforms compression method into a `&str`.
    pub fn as_str(&self) -> Option<&'static str> {
        return match self {
            &Compression::Lz4 => Some(LZ4),
            &Compression::Snappy => Some(SNAPPY),
            &Compression::None => None,
        };
    }

    fn encode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut encoder = snap::Encoder::new();
        return encoder.compress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn decode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut decoder = snap::Decoder::new();
        return decoder.decompress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn encode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        return Ok(lz4::compress(bytes.as_slice()));
    }

    fn decode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        // skip first 4 bytes in accordance to
        // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L805
        return lz4::decompress(&bytes[4..])
            .map_err(|err| CompressionError::Lz4(err.description().to_string()));
    }
}

impl From<String> for Compression {
    /// It converts `String` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_string: String) -> Compression {
        return Compression::from(compression_string.as_str());
    }
}

impl<'a> From<&'a str> for Compression {
    /// It converts `str` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_str: &'a str) -> Compression {
        return match compression_str {
            LZ4 => Compression::Lz4,
            SNAPPY => Compression::Snappy,
            _ => Compression::None,
        };
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_compression_from_str() {
        let lz4 = "lz4";
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy";
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x";
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_from_string() {
        let lz4 = "lz4".to_string();
        assert_eq!(Compression::from(lz4), Compression::Lz4);
        let snappy = "snappy".to_string();
        assert_eq!(Compression::from(snappy), Compression::Snappy);
        let none = "x".to_string();
        assert_eq!(Compression::from(none), Compression::None);
    }

    #[test]
    fn test_compression_encode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_snappy() {
        let snappy_compression = Compression::Snappy;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = snappy_compression.encode(bytes.clone()).unwrap();
        assert_eq!(snappy_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_lz4() {
        let snappy_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        snappy_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_lz4() {
        let lz4_compression = Compression::Lz4;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let len = encoded.len() as u8;
        let mut input = vec![0, 0, 0, len];
        input.extend_from_slice(encoded.as_slice());
        assert_eq!(lz4_compression.decode(input).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        none_compression.encode(bytes.clone()).expect("Should work without exceptions");
    }

    #[test]
    fn test_compression_decode_none() {
        let none_compression = Compression::None;
        let bytes = String::from("Hello World").into_bytes().to_vec();
        let encoded = none_compression.encode(bytes.clone()).unwrap();
        assert_eq!(none_compression.decode(encoded).unwrap(), bytes);
    }

    #[test]
    fn test_compression_encode_snappy_with_non_utf8() {
        let lz4_compression = Compression::Lz4;
        let bytes: Vec<u8> = vec![0x7f, 0x7f, 0x7f, 0x7f, 0x7f];
        let encoded = lz4_compression.encode(bytes.clone()).unwrap();
        let decode = lz4_compression.decode(encoded);
        assert_eq!(decode.is_err(), true);
    }

}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate glium;
extern crate image;

use core::shader::PTCVertex;
use glium::index;
use glium::Surface;

pub fn build_window() -> glium::backend::glutin_backend::GlutinFacade { //glium::backend::glutin_backend::GlutinFacade
  use glium::DisplayBuild;
  use glium::glutin::GlRequest;
  use glium::glutin::GlProfile;
  use glium::glutin::Api;
  use glium::glutin::WindowBuilder;

  WindowBuilder::new()
   .with_gl_profile(GlProfile::Core)
   .with_gl(GlRequest::Specific(Api::OpenGl,(4,0)))
   .build_glium()
   .unwrap()
}

pub fn render_state<F>(display : &F) -> RenderState where F : glium::backend::Facade {
  use core::shader;
  use std::path::Path;

  let image = image::open(&Path::new("img/small.scene.png")).unwrap().to_rgba();
  let image_dimensions = image.dimensions();
  let image_raw = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
  let texture_array = glium::texture::Texture2dArray::new(display, vec![image_raw]).unwrap();

  RenderState {
    program: shader::simple_program(display),
    texture: texture_array,
  }
}

pub struct RenderState {
  pub program: glium::Program,
  pub texture: glium::texture::texture2d_array::Texture2dArray,
}

pub struct QuadTesselator<T> {
  vertices: Vec<T>,
  n: u32,
}

impl<T : Copy> QuadTesselator<T> {
  fn add(&mut self, t:T) {
    self.vertices.push(t);
    self.n += 1;
    if self.n % 4 == 0 {
      let va = self.vertices[self.vertices.len() - 4];
      self.vertices.push(va);

      let vb = self.vertices[self.vertices.len() - 3];
      self.vertices.push(vb);
    }
  }

  fn clear(&mut self) {
    self.vertices.clear();
    self.n = 0;
  }
}

pub fn render(display : &glium::Display , rs:&RenderState) -> () {
  let mut quad_tesselator : QuadTesselator<PTCVertex> = QuadTesselator { vertices: Vec::new(), n: 0};
  quad_tesselator.add(PTCVertex { position: [-0.5, -0.5, 0.0], tex_coord: [0.0, 0.0, 0.0], color: [0.0, 1.0, 0.0, 1.0] });
  quad_tesselator.add(PTCVertex { position: [-0.5,  0.5, 0.0], tex_coord: [0.0, 1.0, 0.0], color: [0.0, 0.0, 1.0, 1.0] });
  quad_tesselator.add(PTCVertex { position: [0.5,  0.5, 0.0], tex_coord: [1.0, 1.0, 0.0], color: [0.0, 0.0, 1.0, 1.0] });
  quad_tesselator.add(PTCVertex { position: [0.5,  -0.5, 0.0], tex_coord: [1.0, 0.0, 0.0], color: [0.0, 0.0, 1.0, 1.0] });

  let vertex_buffer = glium::VertexBuffer::new(display,&quad_tesselator.vertices).unwrap();

  let uniforms = uniform! {
    matrix: [
      [1.0, 0.0, 0.0, 0.0],
      [0.0, 1.0, 0.0, 0.0],
      [0.0, 0.0, 1.0, 0.0],
      [0.0, 0.0, 0.0, 1.0f32]
    ],
    textureArray: &rs.texture
  };

  // drawing a frame
  let mut target = display.draw();
  target.clear_color(0.0, 0.0, 0.0, 0.0);

  target.draw(&vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &rs.program, &uniforms, &Default::default()).unwrap();
  target.finish().unwrap();

  ()
}<commit_msg>Quad tesselator now only supports quads (surprise)<commit_after>#![allow(dead_code)]

extern crate glium;
extern crate image;

use core::shader::PTCVertex;
use glium::index;
use glium::Surface;

pub fn build_window() -> glium::backend::glutin_backend::GlutinFacade { //glium::backend::glutin_backend::GlutinFacade
  use glium::DisplayBuild;
  use glium::glutin::GlRequest;
  use glium::glutin::GlProfile;
  use glium::glutin::Api;
  use glium::glutin::WindowBuilder;

  WindowBuilder::new()
   .with_gl_profile(GlProfile::Core)
   .with_gl(GlRequest::Specific(Api::OpenGl,(4,0)))
   .build_glium()
   .unwrap()
}

pub fn render_state<F>(display : &F) -> RenderState where F : glium::backend::Facade {
  use core::shader;
  use std::path::Path;

  let image = image::open(&Path::new("img/small.scene.png")).unwrap().to_rgba();
  let image_dimensions = image.dimensions();
  let image_raw = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
  let texture_array = glium::texture::Texture2dArray::new(display, vec![image_raw]).unwrap();

  RenderState {
    program: shader::simple_program(display),
    texture: texture_array,
  }
}

pub struct RenderState {
  pub program: glium::Program,
  pub texture: glium::texture::texture2d_array::Texture2dArray,
}

pub struct QuadTesselator<T> {
  vertices: Vec<T>,
  n: u32,
}

impl<T : Copy> QuadTesselator<T> {
  fn add_quad(&mut self, ts:[T; 4]) {
    for ele in ts.iter() {
      self.vertices.push(ele.clone());
    }
    let va = self.vertices[self.vertices.len() - 4];
    self.vertices.push(va);
    let vb = self.vertices[self.vertices.len() - 3];
    self.vertices.push(vb);
  }

  fn clear(&mut self) {
    self.vertices.clear();
  }
}

pub fn render(display : &glium::Display , rs:&RenderState) -> () {
  let mut quad_tesselator : QuadTesselator<PTCVertex> = QuadTesselator { vertices: Vec::new(), n: 0};

  quad_tesselator.add_quad(
    [
      PTCVertex { position: [-0.5, -0.5, 0.0], tex_coord: [0.0, 0.0, 0.0], color: [0.5, 1.0, 0.5, 1.0] },
      PTCVertex { position: [-0.5,  0.5, 0.0], tex_coord: [0.0, 1.0, 0.0], color: [0.25, 0.25, 1.0, 1.0] },
      PTCVertex { position: [0.5,   0.5, 0.0], tex_coord: [1.0, 1.0, 0.0], color: [1.0, 0.5, 0.5, 1.0] },
      PTCVertex { position: [0.5,  -0.5, 0.0], tex_coord: [1.0, 0.0, 0.0], color: [0.5, 0.1, 1.0, 1.0] }
    ]
  );

  let vertex_buffer = glium::VertexBuffer::new(display,&quad_tesselator.vertices).unwrap();

  let uniforms = uniform! {
    matrix: [
      [1.0, 0.0, 0.0, 0.0],
      [0.0, 1.0, 0.0, 0.0],
      [0.0, 0.0, 1.0, 0.0],
      [0.0, 0.0, 0.0, 1.0f32]
    ],
    textureArray: &rs.texture
  };

  // drawing a frame
  let mut target = display.draw();
  target.clear_color(0.0, 0.0, 0.0, 0.0);

  target.draw(&vertex_buffer, &index::NoIndices(index::PrimitiveType::TrianglesList), &rs.program, &uniforms, &Default::default()).unwrap();
  target.finish().unwrap();

  ()
}<|endoftext|>


<commit_before>//! List groups
//!
//! https://docs.gitlab.com/ce/api/groups.html#list-groups
//!
//! # List groups
//!
//! Get a list of groups. (As user: my groups or all available, as admin: all groups).
//!
//! ```text
//! GET /groups
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `skip_groups` | array of integers | no | Skip the group IDs passes |
//! | `all_available` | boolean | no | Show all the groups you have access to |
//! | `search` | string | no | Return list of authorized groups matching the search criteria |
//! | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
//! | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
//!
//! You can search for groups by name or path.
//!

use BuildQuery;

pub mod owned_groups;
pub mod projects;
pub mod details;

// TODO:
// New group:                   POST /groups
// Transfer project to group:   POST  /groups/:id/projects/:project_id
// Update group:                PUT /groups/:id
// Remove group:                DELETE /groups/:id

#[derive(Debug, Copy, Clone)]
pub enum ListingOrderBy {
    Name,
    Path,
}

#[derive(Debug, Copy, Clone)]
pub enum ListingSort {
    Asc,
    Desc,
}

fn append_group_lister_options_order_by(order_by: ListingOrderBy, s: &mut String) {
    s.push_str(match order_by {
        ListingOrderBy::Name => "name",
        ListingOrderBy::Path => "path",
    });
}

fn append_group_lister_options_sort(order_by: ListingSort, s: &mut String) {
    s.push_str(match order_by {
        ListingSort::Asc => "asc",
        ListingSort::Desc => "desc",
    });
}

/// https://docs.gitlab.com/ce/api/groups.html#list-groups
#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// Skip the group IDs passes
    skip_groups: Vec<i64>,
    /// Show all the groups you have access to
    all_available: Option<bool>,
    /// Return list of authorized groups matching the search criteria
    search: String,
    /// Order groups by `name` or `path`. Default is `name`
    order_by: Option<ListingOrderBy>,
    /// Order groups in `asc` or `desc` order. Default is `asc`
    sort: Option<ListingSort>,
}

#[allow(dead_code)]
impl Listing {
    pub fn new() -> Listing {
        Default::default()
    }
    pub fn skip_groups(&mut self, skip_groups: Vec<i64>) -> &mut Listing {
        self.skip_groups = skip_groups;
        self
    }
    pub fn all_available(&mut self, all_available: bool) -> &mut Listing {
        self.all_available = Some(all_available);
        self
    }
    pub fn search(&mut self, search: String) -> &mut Listing {
        self.search = search;
        self
    }
    pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
        self.order_by = Some(order_by);
        self
    }
    fn sort(&mut self, sort: ListingSort) -> &mut Listing {
        self.sort = Some(sort);
        self
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = String::from("groups");

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?", only if one of the `Option` is `Some(_)`
        query.push_str(match (self.skip_groups.is_empty(),
                              &self.all_available,
                              self.search.is_empty(),
                              &self.order_by,
                              &self.sort) {
            (true, &None, true, &None, &None) => "",
            _ => "?",
        });

        if !self.skip_groups.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            let mut array_split_char = &none_char;
            for skip_group in &self.skip_groups {
                query.push_str(array_split_char);
                query.push_str("skip_groups[]=");
                query.push_str(&skip_group.to_string());
                array_split_char = &amp_char;
            }
        }

        self.all_available.map(|all_available| {
            query.push_str(split_char);
            split_char = &amp_char;

            if all_available {
                query.push_str("all_available=true")
            } else {
                query.push_str("all_available=false")
            }
        });

        if !self.search.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("search=");
            query.push_str(&self.search);
        }

        self.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            append_group_lister_options_order_by(order_by, &mut query);
        });

        self.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            append_group_lister_options_sort(sort, &mut query);
        });

        query
    }
}

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

    #[test]
    fn groups_build_query_default() {
        let expected_string = "groups";
        let listing: Listing = Default::default();
        let query = listing.build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups";
        let listing = Listing::new();
        let query = listing.build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_skip_groups() {
        let expected_string = "groups?skip_groups[]=1&skip_groups[]=2&skip_groups[]=3";
        let query = Listing::new().skip_groups(vec![1, 2, 3]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_all_available() {
        let expected_string = "groups?all_available=true";
        let query = Listing::new().all_available(true).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups?all_available=false";
        let query = Listing::new().all_available(false).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_search() {
        let expected_string = "groups?search=SearchPattern";
        let query = Listing::new().search(String::from("SearchPattern")).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_order_by_name() {
        let expected_string = "groups?order_by=name";
        let query = Listing::new().order_by(ListingOrderBy::Name).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_order_by_path() {
        let expected_string = "groups?order_by=path";
        let query = Listing::new().order_by(ListingOrderBy::Path).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_sort() {
        let expected_string = "groups?sort=asc";
        let query = Listing::new().sort(ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups?sort=desc";
        let query = Listing::new().sort(ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_search_order_by_path() {
        let expected_string = "groups?search=SearchPattern&order_by=path";
        let query = Listing::new()
            .order_by(ListingOrderBy::Path)
            .search(String::from("SearchPattern"))
            .build_query();
        assert_eq!(query, expected_string);
        let query = Listing::new()
            .search(String::from("SearchPattern"))
            .order_by(ListingOrderBy::Path)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Note about _Search for group_<commit_after>//! List groups
//!
//! https://docs.gitlab.com/ce/api/groups.html#list-groups
//!
//! # List groups
//!
//! Get a list of groups. (As user: my groups or all available, as admin: all groups).
//!
//! ```text
//! GET /groups
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `skip_groups` | array of integers | no | Skip the group IDs passes |
//! | `all_available` | boolean | no | Show all the groups you have access to |
//! | `search` | string | no | Return list of authorized groups matching the search criteria |
//! | `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
//! | `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
//!
//! You can search for groups by name or path.
//!
//! **NOTE**: The _Search for group_ (from
//! https://docs.gitlab.com/ce/api/groups.html#search-for-group) is performed in this module.
//!
//!

use BuildQuery;

pub mod owned_groups;
pub mod projects;
pub mod details;

// TODO:
// New group:                   POST /groups
// Transfer project to group:   POST  /groups/:id/projects/:project_id
// Update group:                PUT /groups/:id
// Remove group:                DELETE /groups/:id

#[derive(Debug, Copy, Clone)]
pub enum ListingOrderBy {
    Name,
    Path,
}

#[derive(Debug, Copy, Clone)]
pub enum ListingSort {
    Asc,
    Desc,
}

fn append_group_lister_options_order_by(order_by: ListingOrderBy, s: &mut String) {
    s.push_str(match order_by {
        ListingOrderBy::Name => "name",
        ListingOrderBy::Path => "path",
    });
}

fn append_group_lister_options_sort(order_by: ListingSort, s: &mut String) {
    s.push_str(match order_by {
        ListingSort::Asc => "asc",
        ListingSort::Desc => "desc",
    });
}

/// https://docs.gitlab.com/ce/api/groups.html#list-groups
#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// Skip the group IDs passes
    skip_groups: Vec<i64>,
    /// Show all the groups you have access to
    all_available: Option<bool>,
    /// Return list of authorized groups matching the search criteria
    search: String,
    /// Order groups by `name` or `path`. Default is `name`
    order_by: Option<ListingOrderBy>,
    /// Order groups in `asc` or `desc` order. Default is `asc`
    sort: Option<ListingSort>,
}

#[allow(dead_code)]
impl Listing {
    pub fn new() -> Listing {
        Default::default()
    }
    pub fn skip_groups(&mut self, skip_groups: Vec<i64>) -> &mut Listing {
        self.skip_groups = skip_groups;
        self
    }
    pub fn all_available(&mut self, all_available: bool) -> &mut Listing {
        self.all_available = Some(all_available);
        self
    }
    pub fn search(&mut self, search: String) -> &mut Listing {
        self.search = search;
        self
    }
    pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
        self.order_by = Some(order_by);
        self
    }
    fn sort(&mut self, sort: ListingSort) -> &mut Listing {
        self.sort = Some(sort);
        self
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = String::from("groups");

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?", only if one of the `Option` is `Some(_)`
        query.push_str(match (self.skip_groups.is_empty(),
                              &self.all_available,
                              self.search.is_empty(),
                              &self.order_by,
                              &self.sort) {
            (true, &None, true, &None, &None) => "",
            _ => "?",
        });

        if !self.skip_groups.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            let mut array_split_char = &none_char;
            for skip_group in &self.skip_groups {
                query.push_str(array_split_char);
                query.push_str("skip_groups[]=");
                query.push_str(&skip_group.to_string());
                array_split_char = &amp_char;
            }
        }

        self.all_available.map(|all_available| {
            query.push_str(split_char);
            split_char = &amp_char;

            if all_available {
                query.push_str("all_available=true")
            } else {
                query.push_str("all_available=false")
            }
        });

        if !self.search.is_empty() {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("search=");
            query.push_str(&self.search);
        }

        self.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            append_group_lister_options_order_by(order_by, &mut query);
        });

        self.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            append_group_lister_options_sort(sort, &mut query);
        });

        query
    }
}

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

    #[test]
    fn groups_build_query_default() {
        let expected_string = "groups";
        let listing: Listing = Default::default();
        let query = listing.build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups";
        let listing = Listing::new();
        let query = listing.build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_skip_groups() {
        let expected_string = "groups?skip_groups[]=1&skip_groups[]=2&skip_groups[]=3";
        let query = Listing::new().skip_groups(vec![1, 2, 3]).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_all_available() {
        let expected_string = "groups?all_available=true";
        let query = Listing::new().all_available(true).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups?all_available=false";
        let query = Listing::new().all_available(false).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_search() {
        let expected_string = "groups?search=SearchPattern";
        let query = Listing::new().search(String::from("SearchPattern")).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_order_by_name() {
        let expected_string = "groups?order_by=name";
        let query = Listing::new().order_by(ListingOrderBy::Name).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_order_by_path() {
        let expected_string = "groups?order_by=path";
        let query = Listing::new().order_by(ListingOrderBy::Path).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_sort() {
        let expected_string = "groups?sort=asc";
        let query = Listing::new().sort(ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "groups?sort=desc";
        let query = Listing::new().sort(ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_search_order_by_path() {
        let expected_string = "groups?search=SearchPattern&order_by=path";
        let query = Listing::new()
            .order_by(ListingOrderBy::Path)
            .search(String::from("SearchPattern"))
            .build_query();
        assert_eq!(query, expected_string);
        let query = Listing::new()
            .search(String::from("SearchPattern"))
            .order_by(ListingOrderBy::Path)
            .build_query();
        assert_eq!(query, expected_string);
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;

pub struct UIBuffer {
    width: uint,
    height: uint,
    rows: Vec<Vec<Cell>>
}

impl UIBuffer {
    pub fn new(width: uint, height: uint) -> UIBuffer {
        let rows = Cell::create_grid(width, height, ' ');

        UIBuffer {
            width: width,
            height: height,
            rows: rows,
        }
    }

    pub fn draw_range(&self, start: uint, stop: uint) {
        let rows = self.rows.slice(start, stop);
        for row in rows.iter() {
            for cell in row.iter() {
                rustbox::print_char(cell.x, cell.y, rustbox::Style::Normal, cell.fg, cell.bg, cell.ch);
            }
        }
    }

    pub fn draw_everything(&self) {
        self.draw_range(0, self.height);
    }

    pub fn get_width(&self) -> uint {
        self.width
    }

    pub fn get_height(&self) -> uint {
        self.height
    }

    /// Recreated the entire grid, will cells containing `ch`.
    pub fn fill(&mut self, ch: char) {
        self.rows = Cell::create_grid(self.width, self.height, ch);
    }

    /// Update the `ch` attribute of an individual cell
    pub fn update_cell_content(&mut self, cell_num: uint, row_num: uint, ch: char) {
        self.rows[row_num][cell_num].ch = ch
    }

    /// Update the `ch`, `fg`, and `bg` attributes of an indivudual cell
    pub fn update_cell(&mut self, cell_num: uint, row_num: uint, ch: char, fg: rustbox::Color, bg: rustbox::Color) {
        let cell = self.get_cell_mut(cell_num, row_num);
        cell.ch = ch;
        cell.fg = fg;
        cell.bg = bg;
    }

    pub fn get_cell_mut(&mut self, cell_num: uint, row_num: uint) -> &mut Cell {
        &mut self.rows[row_num][cell_num]
    }
}

pub struct Cell {
    pub bg: rustbox::Color,
    pub fg: rustbox::Color,
    pub ch: char,
    pub x: uint,
    pub y: uint,
}

impl Cell {
    pub fn new() -> Cell {
        Cell {
            bg: rustbox::Color::Default,
            fg: rustbox::Color::White,
            ch: ' ',
            x: 0,
            y: 0,
        }
    }

    pub fn create_grid(width: uint, height: uint, ch: char) -> Vec<Vec<Cell>> {
        let mut rows = Vec::new();
        for voffset in range(0, height) {
            let mut cells = Vec::new();
            for boffset in range(0, width) {
                let mut cell = Cell::new();
                cell.x = boffset;
                cell.y = voffset;
                cell.ch = ch;
                cells.push(cell);
            }
            rows.push(cells);
        }

        return rows
    }
}

#[cfg(test)]
mod tests {

    extern crate rustbox;

    use uibuf::UIBuffer;

    fn setup_uibuf() -> UIBuffer {
        UIBuffer::new(50, 50)
    }

    #[test]
    fn grid_is_created_correctly() {
        let uibuf = setup_uibuf();
        assert_eq!(uibuf.height, 50);
        assert_eq!(uibuf.width, 50);
        assert_eq!(uibuf.rows.len(), 50);
        assert_eq!(uibuf.rows[0][5].ch, ' ');
    }

    #[test]
    fn fill_updates_contents_of_all_cells() {
        let mut uibuf = setup_uibuf();
        uibuf.fill('x');

        // check some random cells
        assert_eq!(uibuf.rows[20][5].ch, 'x');
        assert_eq!(uibuf.rows[0][30].ch, 'x');
    }

    #[test]
    fn update_cell_content_updates_a_single_cell() {
        let mut uibuf = setup_uibuf();
        // update cell 10 in row 32
        uibuf.update_cell_content(10, 32, 'y');

        assert_eq!(uibuf.rows[32][10].ch, 'y');
    }

    #[test]
    fn update_cell_updates_all_attrs_of_cell() {
        let mut uibuf = setup_uibuf();
        let cell_num = 10u;
        let row_num = 0u;
        let ch = 'q';
        let fg = rustbox::Color::Red;
        let bg = rustbox::Color::Blue;

        uibuf.update_cell(cell_num, row_num, ch, fg, bg);

        let cell = &uibuf.rows[row_num][cell_num];
        assert_eq!(cell.ch, ch);

        // FIXME(greg): this fails cos '==' is not implemented for rustbox::Color.
        // assert_eq!(cell.fg, fg);
        // assert_eq!(cell.bg, bg);
    }

}
<commit_msg>Use default color for text foreground - fixes #34<commit_after>extern crate rustbox;

pub struct UIBuffer {
    width: uint,
    height: uint,
    rows: Vec<Vec<Cell>>
}

impl UIBuffer {
    pub fn new(width: uint, height: uint) -> UIBuffer {
        let rows = Cell::create_grid(width, height, ' ');

        UIBuffer {
            width: width,
            height: height,
            rows: rows,
        }
    }

    pub fn draw_range(&self, start: uint, stop: uint) {
        let rows = self.rows.slice(start, stop);
        for row in rows.iter() {
            for cell in row.iter() {
                rustbox::print_char(cell.x, cell.y, rustbox::Style::Normal, cell.fg, cell.bg, cell.ch);
            }
        }
    }

    pub fn draw_everything(&self) {
        self.draw_range(0, self.height);
    }

    pub fn get_width(&self) -> uint {
        self.width
    }

    pub fn get_height(&self) -> uint {
        self.height
    }

    /// Recreated the entire grid, will cells containing `ch`.
    pub fn fill(&mut self, ch: char) {
        self.rows = Cell::create_grid(self.width, self.height, ch);
    }

    /// Update the `ch` attribute of an individual cell
    pub fn update_cell_content(&mut self, cell_num: uint, row_num: uint, ch: char) {
        self.rows[row_num][cell_num].ch = ch
    }

    /// Update the `ch`, `fg`, and `bg` attributes of an indivudual cell
    pub fn update_cell(&mut self, cell_num: uint, row_num: uint, ch: char, fg: rustbox::Color, bg: rustbox::Color) {
        let cell = self.get_cell_mut(cell_num, row_num);
        cell.ch = ch;
        cell.fg = fg;
        cell.bg = bg;
    }

    pub fn get_cell_mut(&mut self, cell_num: uint, row_num: uint) -> &mut Cell {
        &mut self.rows[row_num][cell_num]
    }
}

pub struct Cell {
    pub bg: rustbox::Color,
    pub fg: rustbox::Color,
    pub ch: char,
    pub x: uint,
    pub y: uint,
}

impl Cell {
    pub fn new() -> Cell {
        Cell {
            bg: rustbox::Color::Default,
            fg: rustbox::Color::Default,
            ch: ' ',
            x: 0,
            y: 0,
        }
    }

    pub fn create_grid(width: uint, height: uint, ch: char) -> Vec<Vec<Cell>> {
        let mut rows = Vec::new();
        for voffset in range(0, height) {
            let mut cells = Vec::new();
            for boffset in range(0, width) {
                let mut cell = Cell::new();
                cell.x = boffset;
                cell.y = voffset;
                cell.ch = ch;
                cells.push(cell);
            }
            rows.push(cells);
        }

        return rows
    }
}

#[cfg(test)]
mod tests {

    extern crate rustbox;

    use uibuf::UIBuffer;

    fn setup_uibuf() -> UIBuffer {
        UIBuffer::new(50, 50)
    }

    #[test]
    fn grid_is_created_correctly() {
        let uibuf = setup_uibuf();
        assert_eq!(uibuf.height, 50);
        assert_eq!(uibuf.width, 50);
        assert_eq!(uibuf.rows.len(), 50);
        assert_eq!(uibuf.rows[0][5].ch, ' ');
    }

    #[test]
    fn fill_updates_contents_of_all_cells() {
        let mut uibuf = setup_uibuf();
        uibuf.fill('x');

        // check some random cells
        assert_eq!(uibuf.rows[20][5].ch, 'x');
        assert_eq!(uibuf.rows[0][30].ch, 'x');
    }

    #[test]
    fn update_cell_content_updates_a_single_cell() {
        let mut uibuf = setup_uibuf();
        // update cell 10 in row 32
        uibuf.update_cell_content(10, 32, 'y');

        assert_eq!(uibuf.rows[32][10].ch, 'y');
    }

    #[test]
    fn update_cell_updates_all_attrs_of_cell() {
        let mut uibuf = setup_uibuf();
        let cell_num = 10u;
        let row_num = 0u;
        let ch = 'q';
        let fg = rustbox::Color::Red;
        let bg = rustbox::Color::Blue;

        uibuf.update_cell(cell_num, row_num, ch, fg, bg);

        let cell = &uibuf.rows[row_num][cell_num];
        assert_eq!(cell.ch, ch);

        // FIXME(greg): this fails cos '==' is not implemented for rustbox::Color.
        // assert_eq!(cell.fg, fg);
        // assert_eq!(cell.bg, bg);
    }

}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]

use std::error::Error;
use std::fmt;
use std::str;
use super::ast::*;

pub fn parse(source: &str) -> Result<Vec<Entry>, ParseError> {
    let mut p = Parser::new(source);
    p.parse()
}

#[derive(Debug)]
pub struct ParseError {
    pub error_message: String,
}

impl ParseError {
    pub fn new(error_message: &str) -> Self {
        ParseError { error_message: String::from(error_message) }
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        &self.error_message
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    fn parse(&mut self) -> Result<Vec<Entry>, ParseError> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            let comment = None;
            match self.get_entry(comment) {
                Ok(entry) => entries.push(entry),
                Err(err) => return Err(err),
            }
            self.get_ws();
        }
        Ok(entries)
    }

    fn get_entry(&mut self, comment: Option<Comment>) -> Result<Entry, ParseError> {
        self.get_entity(comment)
    }

    fn get_entity(&mut self, comment: Option<Comment>) -> Result<Entry, ParseError> {
        let id = try!(self.get_identifier());
        self.get_line_ws();

        if !self.ch_is('=') {
            return Err(ParseError::new("Expected '='"));
        }
        self.bump();

        self.get_line_ws();

        match self.get_pattern() {
            Ok(value) => {
                Ok(Entry::Entity {
                    id: id,
                    comment: comment,
                    value: value,
                })
            }
            Err(err) => Err(err),
        }
    }

    fn get_identifier(&mut self) -> Result<Identifier, ParseError> {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => return Err(ParseError::new("Unexpected end of input.")),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Ok(Identifier { name: name }),
        }
        self.bump();

        loop {
            let ch = match self.ch {
                Some(c) => c,
                None => break,
            };

            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Ok(Identifier { name: name })
    }

    fn get_pattern(&mut self) -> Result<Value, ParseError> {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        return Err(ParseError::new("Unclosed string"));
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && buffer.len() != 0 {
                        return Err(ParseError::new("Multiline string should have the ID line \
                                                    empty"));
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if buffer.len() != 0 {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            return Err(ParseError::new("Unclosed string"));
        }

        if buffer.len() != 0 {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.len() == 0 {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Ok(Value::Pattern {
            source: source,
            elements: content,
        })
    }
}

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

    fn expected_parse(name: &str, source: &str) {
        match parse(source) {
            Err(e) => panic!("Parse failed: {}: {}", name, e),
            _ => {}
        }
    }

    fn expected_failure(name: &str, source: &str) {
        match parse(source) {
            Ok(_) => panic!("Parse unexpectedly worked: {}", name),
            _ => {}
        }
    }

    #[test]
    fn it_works() {
        expected_parse("simple", "a = b");
        expected_parse("simple", "a=b");
        expected_parse("simple", "a   =     b");
        expected_parse("multiline",
                       "multi =\n\
                        | abc\n\
                       ");
        expected_failure("comment", "#comment");
        expected_failure("comment", "# comment");
        expected_failure("comment", "#  comment");
    }
}
<commit_msg>l20n: Fix Clippy warnings.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(missing_docs)]

use std::error::Error;
use std::fmt;
use std::str;
use super::ast::*;

pub fn parse(source: &str) -> Result<Vec<Entry>, ParseError> {
    let mut p = Parser::new(source);
    p.parse()
}

#[derive(Debug)]
pub struct ParseError {
    pub error_message: String,
}

impl ParseError {
    pub fn new(error_message: &str) -> Self {
        ParseError { error_message: String::from(error_message) }
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        &self.error_message
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

struct Parser<'a> {
    source: str::Chars<'a>,
    ch: Option<char>,
    pos: u16,
}

impl<'a> Parser<'a> {
    fn new(source: &'a str) -> Parser<'a> {
        Parser {
            source: source.chars(),
            ch: None,
            pos: 0,
        }
    }

    fn bump(&mut self) {
        self.ch = self.source.next();

        self.pos += 1;
    }

    fn ch_is(&self, ch: char) -> bool {
        self.ch == Some(ch)
    }

    fn get_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\n') || self.ch_is('\t') || self.ch_is('\r') {
            self.bump();
        }
    }

    fn get_line_ws(&mut self) {
        while self.ch_is(' ') || self.ch_is('\t') {
            self.bump();
        }
    }

    fn parse(&mut self) -> Result<Vec<Entry>, ParseError> {
        let mut entries: Vec<Entry> = Vec::new();

        self.get_ws();

        self.bump();

        loop {
            if self.ch == None {
                break;
            }

            let comment = None;
            match self.get_entry(comment) {
                Ok(entry) => entries.push(entry),
                Err(err) => return Err(err),
            }
            self.get_ws();
        }
        Ok(entries)
    }

    fn get_entry(&mut self, comment: Option<Comment>) -> Result<Entry, ParseError> {
        self.get_entity(comment)
    }

    fn get_entity(&mut self, comment: Option<Comment>) -> Result<Entry, ParseError> {
        let id = try!(self.get_identifier());
        self.get_line_ws();

        if !self.ch_is('=') {
            return Err(ParseError::new("Expected '='"));
        }
        self.bump();

        self.get_line_ws();

        match self.get_pattern() {
            Ok(value) => {
                Ok(Entry::Entity {
                    id: id,
                    comment: comment,
                    value: value,
                })
            }
            Err(err) => Err(err),
        }
    }

    fn get_identifier(&mut self) -> Result<Identifier, ParseError> {
        let mut name = String::new();

        let ch = match self.ch {
            Some(c) => c,
            None => return Err(ParseError::new("Unexpected end of input.")),
        };

        match ch {
            'a'...'z' | 'A'...'Z' | '_' => name.push(ch),
            _ => return Ok(Identifier { name: name }),
        }
        self.bump();

        while let Some(ch) = self.ch {
            match ch {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '_' | '-' => name.push(ch),
                _ => break,
            }
            self.bump();
        }

        Ok(Identifier { name: name })
    }

    fn get_pattern(&mut self) -> Result<Value, ParseError> {
        let mut buffer = String::new();
        let mut source = String::new();
        let mut content = vec![];
        let mut quote_delimited: bool = false;
        let mut first_line = true;

        if self.ch_is('"') {
            quote_delimited = true;
        }

        loop {
            match self.ch {
                Some(c) if c == '\n' => {
                    if quote_delimited {
                        return Err(ParseError::new("Unclosed string"));
                    }
                    self.bump();
                    self.get_line_ws();

                    if !self.ch_is('|') {
                        break;
                    }
                    if first_line && !buffer.is_empty() {
                        return Err(ParseError::new("Multiline string should have the ID line \
                                                    empty"));
                    }
                    first_line = false;
                    self.bump();
                    if self.ch_is(' ') {
                        self.bump();
                    }
                    if !buffer.is_empty() {
                        buffer.push('\n');
                    }
                    continue;
                }
                Some(c) if c == '"' => {
                    self.bump();
                    quote_delimited = false;
                    break;
                }
                Some(c) => source.push(c),
                None => break,
            }
            match self.ch {
                Some(c) => buffer.push(c),
                None => continue,
            };
            self.bump();
        }

        if quote_delimited {
            return Err(ParseError::new("Unclosed string"));
        }

        if !buffer.is_empty() {
            // source.append(buffer);
            content.push(PatternElement::TextElement { value: source.clone() });
        }

        if content.is_empty() {
            // return Value::Pattern(source: source, elements: content);
        }

        content.push(PatternElement::TextElement { value: source.clone() });

        Ok(Value::Pattern {
            source: source,
            elements: content,
        })
    }
}

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

    fn expected_parse(name: &str, source: &str) {
        match parse(source) {
            Err(e) => panic!("Parse failed: {}: {}", name, e),
            _ => {}
        }
    }

    fn expected_failure(name: &str, source: &str) {
        match parse(source) {
            Ok(_) => panic!("Parse unexpectedly worked: {}", name),
            _ => {}
        }
    }

    #[test]
    fn it_works() {
        expected_parse("simple", "a = b");
        expected_parse("simple", "a=b");
        expected_parse("simple", "a   =     b");
        expected_parse("multiline",
                       "multi =\n\
                        | abc\n\
                       ");
        expected_failure("comment", "#comment");
        expected_failure("comment", "# comment");
        expected_failure("comment", "#  comment");
    }
}
<|endoftext|>


<commit_before>#[deny(non_camel_case_types)];
/// Additional general-purpose comparison functionality.

const fuzzy_epsilon: float = 1.0e-6;

trait FuzzyEq {
    pure fn fuzzy_eq(&&other: self) -> bool;
}

impl float: FuzzyEq {
    pure fn fuzzy_eq(&&other: float) -> bool {
        return float::abs(self - other) < fuzzy_epsilon;
    }
}

impl f32: FuzzyEq {
    pure fn fuzzy_eq(&&other: f32) -> bool {
        return f32::abs(self - other) < (fuzzy_epsilon as f32);
    }
}

impl f64: FuzzyEq {
    pure fn fuzzy_eq(&&other: f64) -> bool {
        return f64::abs(self - other) < (fuzzy_epsilon as f64);
    }
}

#[test]
fn test_fuzzy_equals() {
    assert ((1.0).fuzzy_eq(1.0));
    assert ((1.0f32).fuzzy_eq(1.0f32));
    assert ((1.0f64).fuzzy_eq(1.0f64));
}

<commit_msg>Remove deprecated modes from FuzzyEq in cmp.rs<commit_after>#[deny(non_camel_case_types)];
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];
/// Additional general-purpose comparison functionality.

const fuzzy_epsilon: float = 1.0e-6;

trait FuzzyEq {
    pure fn fuzzy_eq(other: &self) -> bool;
}

impl float: FuzzyEq {
    pure fn fuzzy_eq(other: &float) -> bool {
        return float::abs(self - *other) < fuzzy_epsilon;
    }
}

impl f32: FuzzyEq {
    pure fn fuzzy_eq(other: &f32) -> bool {
        return f32::abs(self - *other) < (fuzzy_epsilon as f32);
    }
}

impl f64: FuzzyEq {
    pure fn fuzzy_eq(other: &f64) -> bool {
        return f64::abs(self - *other) < (fuzzy_epsilon as f64);
    }
}

#[test]
fn test_fuzzy_equals() {
    assert ((&1.0).fuzzy_eq(&1.0));
    assert ((&1.0f32).fuzzy_eq(&1.0f32));
    assert ((&1.0f64).fuzzy_eq(&1.0f64));
}

<|endoftext|>


<commit_before>use goblin::{container, error};
use mach;
use mach::header;
use mach::load_command;
use mach::exports::{Export};

use Opt;

use prettytable::cell::Cell;
use prettytable::row::Row;
use std::io::{self, Write};
use atty;
use termcolor::*;
use termcolor::Color::*;

use format::*;

pub struct Mach<'a>(pub mach::MachO<'a>, pub Opt);

impl<'a> Mach<'a> {
    pub fn print(&self) -> error::Result<()> {
        let mach = &self.0;
        let args = &self.1;

        let cc = if args.color || atty::is(atty::Stream::Stdout) { ColorChoice::Auto } else { ColorChoice::Never };
        let writer = BufferWriter::stdout(cc);
        let fmt = &mut writer.buffer();

        let header = &mach.header;
        let endianness = if header.is_little_endian() { "little-endian" } else { "big-endian" };
        let machine = header.cputype;
        let kind = |fmt: &mut Buffer, header: &header::Header| {
            let typ_cell = header.filetype;
            let kind_str = header::filetype_to_str(typ_cell);
            match typ_cell {
                header::MH_OBJECT =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_EXECUTE => fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Red)).set_fg(Some(Black)))?,
                header::MH_DYLIB =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_DYLINKER =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_DYLIB_STUB =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Blue)).set_fg(Some(Black)))?,
                header::MH_DSYM =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Green)).set_fg(Some(Black)))?,
                header::MH_CORE => fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(White)).set_fg(Some(Black)))?,
                _ => ()
            }
            write!(fmt, "{}", kind_str)?;
            fmt.reset()
        };
        fmt_hdr(fmt, "Mach-o ")?;
        kind(fmt, &mach.header)?;
        write!(fmt, " ")?;
        fmt_name_bold(fmt, mach::constants::cputype::get_arch_name_from_types(machine,header.cpusubtype).unwrap_or("None"))?;
        write!(fmt, "-{} @ ", endianness)?;
        fmt_addrx(fmt, mach.entry as u64)?;
        writeln!(fmt, ":")?;
        writeln!(fmt, "")?;

        let lcs = &mach.load_commands;
        fmt_header(fmt, "LoadCommands", lcs.len())?;
        for (i, lc) in lcs.into_iter().enumerate() {
            fmt_idx(fmt, i)?;
            write!(fmt, " ")?;
            let name = load_command::cmd_to_str(lc.command.cmd());
            let name = &format!("{:.27}", name);
            match lc.command {
                load_command::CommandVariant::Segment32        (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Segment64        (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Symtab           (_command) => fmt_name_color(fmt, name, Yellow)?,
                load_command::CommandVariant::Dysymtab         (_command) => fmt_name_color(fmt, name, Green)?,
                load_command::CommandVariant::LoadDylinker     (_command) => fmt_name_color(fmt, name, Yellow)?,
                load_command::CommandVariant::LoadDylib        (_command)
                    | load_command::CommandVariant::LoadUpwardDylib(_command)
                    | load_command::CommandVariant::ReexportDylib  (_command)
                    | load_command::CommandVariant::LazyLoadDylib  (_command) => fmt_name_color(fmt, name, Blue)?,
                load_command::CommandVariant::DyldInfo         (_command)
                    | load_command::CommandVariant::DyldInfoOnly   (_command) => fmt_name_color(fmt, name, Cyan)?,
                load_command::CommandVariant::Unixthread       (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Main             (_command) => fmt_name_color(fmt, name, Red)?,
                _ => fmt_name_bold(fmt, name)?
            }
            writeln!(fmt, "")?;
        }

        writeln!(fmt, "")?;

        let segments = &mach.segments;
        fmt_header(fmt, "Segments", segments.len())?;
        for (ref _i, ref segment) in segments.into_iter().enumerate() {
            let name = segment.name().unwrap();
            let sections = &segment.sections().unwrap();
            let mut segment_table = new_table(row![b->"Segment", b->"# Sections"]);
            segment_table.add_row(Row::new(vec![
                str_cell(&name),
                Cell::new(&sections.len().to_string()),
            ]));
            flush(fmt, &writer, segment_table, args.color)?;

            let mut section_table = new_table(row![b->"", b->"Idx", b->"Name", b->"Addr", b->"Size", b->"Offset", b->"Align", b->"Reloff", b->"Nrelocs", b->"Flags"]);
            for (i, &(ref section, _)) in sections.into_iter().enumerate() {
                if let Ok(name) = section.name() {
                    section_table.add_row(Row::new(vec![
                        Cell::new(&format!("{:4}", "")), // filler
                        Cell::new(&i.to_string()),
                        Cell::new(name).style_spec("Fyb"),
                        addrx_cell(section.addr),
                        sz_cell(section.size),
                        offsetx_cell(section.offset as u64),
                        Cell::new(&format!("{}", section.align)),
                        offsetx_cell(section.reloff as u64),
                        Cell::new(&format!("{}", section.nreloc)),
                        Cell::new(&format!("{:#x}", section.flags)),
                    ]));
                } else {
                    section_table.add_row(Row::new(vec![
                        Cell::new(&i.to_string()),
                        Cell::new("BAD SECTION NAME"),
                    ]));
                }
            }
            flush(fmt, &writer, section_table, args.color)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        let mut relocations: Vec<_> = Vec::new();
        let mut nrelocs = 0;
        let r = mach.relocations().unwrap();
        for (_i, relocs, section) in r.into_iter() {
            let section_name = section.name().unwrap();
            let segment_name = section.segname().unwrap();
            let mut rs = Vec::new();
            for reloc in relocs {
                let reloc = reloc.unwrap();
                nrelocs += 1;
                rs.push(reloc);
            }
            if !rs.is_empty() { relocations.push((segment_name.to_owned(), section_name.to_owned(), rs)) };
        }
        // need this to print relocation references
        let symbols = mach.symbols().collect::<Vec<_>>();
        let sections = mach.segments.sections().flat_map(|x| x).map(|s| s.unwrap().0).collect::<Vec<_>>();

        fmt_header(fmt, "Relocations", nrelocs)?;
        for (n1, n2, relocs) in relocations {
            let mut section_table = new_table(row![b->"Segment", b->"Section", b->"Count"]);
            section_table.add_row(Row::new(vec![
                str_cell(&n1),
                str_cell(&n2),
                Cell::new(&relocs.len().to_string()),
            ]));
            flush(fmt, &writer, section_table, args.color)?;
            let mut reloc_table = new_table(row![b->"", b->"Type", b->"Offset", b->"Length", b->"PIC", b->"Extern", b->"SymbolNum", b->"Symbol"]);
            for reloc in relocs {
                let idx = reloc.r_symbolnum();
                let name_cell = {
                    if reloc.is_extern() {
                        // FIXME: i cannot currently get this to compile without iterating and doing all this nonsense, otherwise move errors...
                        let mut maybe_name = None;
                        for (i, symbol) in symbols.iter().enumerate() {
                            match symbol {
                                &Ok((ref name, _)) => {
                                    let name: &str = name;
                                    if i == idx {
                                        maybe_name = Some(name);
                                    }
                                },
                                &Err(_) => ()
                            }
                        }
                        match maybe_name {
                            Some(name) => string_cell(&args, name),
                            None => {
                                cell("None").style_spec("b")
                            },
                        }
                    // not extern so the symbol num should reference a section
                    } else {
                        cell(sections[idx - 1 as usize].name().unwrap_or("None")).style_spec("bi")

                    }
                };
                reloc_table.add_row(Row::new(vec![
                    Cell::new(&format!("{:4}", "")),
                    cell(reloc.to_str(machine)),
                    addrx_cell(reloc.r_address as u64),
                    cell(reloc.r_length()),
                    bool_cell(reloc.is_pic()),
                    bool_cell(reloc.is_extern()),
                    offsetx_cell(idx as u64),
                    name_cell,
                ]));
            }

            flush(fmt, &writer, reloc_table, args.color)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        fmt_header(fmt, "Symbols", symbols.len())?;
        let mut symbol_table = new_table(row![b->"Offset", b->"Name", b->"Section", b->"Global", b->"Undefined"]);
        for (i, symbol) in symbols.into_iter().enumerate() {
            match symbol {
                Ok((name, symbol)) => {
                    let section_cell = if symbol.get_type() == mach::symbols::N_SECT {
                        // we subtract 1 because when N_SECT it is an ordinal, and hence indexing starts from 1
                        cell(sections[symbol.n_sect - 1 as usize].name().unwrap()).style_spec("b")
                    } else {
                        cell("None").style_spec("i")
                    };
                    symbol_table.add_row(Row::new(vec![
                        addrx_cell(symbol.n_value as u64),
                        string_cell(&args, name),
                        section_cell,
                        bool_cell(symbol.is_global()),
                        bool_cell(symbol.is_undefined()),
                    ]));
                },
                Err(e) => {
                    writeln!(fmt, "  {}: {}", i, e)?;
                }
            }
        }
        flush(fmt, &writer, symbol_table, args.color)?;
        writeln!(fmt)?;

        let fmt_exports = |fmt: &mut Buffer, name: &str, syms: &[Export] | -> io::Result<()> {
            fmt_header(fmt, name, syms.len())?;
            for sym in syms {
                fmt_addr_right(fmt, sym.offset)?;
                write!(fmt, " ")?;
                fmt_string(fmt, args, &sym.name)?;
                write!(fmt, " (")?;
                fmt_sz(fmt, sym.size as u64)?;
                writeln!(fmt, ")")?;
            }
            writeln!(fmt, "")
        };

        let exports = match mach.exports () { Ok(exports) => exports, Err(_) => Vec::new() };
        fmt_exports(fmt, "Exports", &exports)?;

        let imports = match mach.imports () { Ok(imports) => imports, Err(_) => Vec::new() };
        fmt_header(fmt, "Imports", imports.len())?;
        for sym in imports {
            fmt_addr_right(fmt, sym.offset)?;
            write!(fmt, " ")?;
            fmt_string(fmt, args, &sym.name)?;
            write!(fmt, " (")?;
            fmt_sz(fmt, sym.size as u64)?;
            write!(fmt, ")")?;
            write!(fmt, " -> ")?;
            fmt_lib(fmt, sym.dylib)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        fmt_header(fmt, "Libraries", mach.libs.len() - 1)?;
        for lib in &mach.libs[1..] {
            fmt_lib_right(fmt, lib)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        write!(fmt, "Name: ")?;
        fmt_str_option(fmt, &mach.name)?;
        writeln!(fmt, "")?;
        write!(fmt, "is_64: ")?;
        fmt_bool(fmt, mach.header.container() == container::Container::Big)?;
        writeln!(fmt, "")?;
        write!(fmt, "is_lib: ")?;
        fmt_bool(fmt, mach.header.filetype == header::MH_DYLIB)?;
        writeln!(fmt, "")?;
        write!(fmt, "little_endian: ")?;
        fmt_bool(fmt, mach.header.is_little_endian())?;
        writeln!(fmt, "")?;
        write!(fmt, "entry: ")?;
        fmt_addr(fmt, mach.entry as u64)?;
        writeln!(fmt, "")?;

        writer.print(&fmt)?;
        Ok(())
    }
}
<commit_msg>mach: print segname.sectname for relocations and symbols<commit_after>use goblin::{container, error};
use mach;
use mach::header;
use mach::load_command;
use mach::exports::{Export};

use Opt;

use prettytable::cell::Cell;
use prettytable::row::Row;
use std::io::{self, Write};
use atty;
use termcolor::*;
use termcolor::Color::*;

use format::*;

pub struct Mach<'a>(pub mach::MachO<'a>, pub Opt);

impl<'a> Mach<'a> {
    pub fn print(&self) -> error::Result<()> {
        let mach = &self.0;
        let args = &self.1;

        let cc = if args.color || atty::is(atty::Stream::Stdout) { ColorChoice::Auto } else { ColorChoice::Never };
        let writer = BufferWriter::stdout(cc);
        let fmt = &mut writer.buffer();

        let header = &mach.header;
        let endianness = if header.is_little_endian() { "little-endian" } else { "big-endian" };
        let machine = header.cputype;
        let kind = |fmt: &mut Buffer, header: &header::Header| {
            let typ_cell = header.filetype;
            let kind_str = header::filetype_to_str(typ_cell);
            match typ_cell {
                header::MH_OBJECT =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_EXECUTE => fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Red)).set_fg(Some(Black)))?,
                header::MH_DYLIB =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_DYLINKER =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Yellow)).set_fg(Some(Black)))?,
                header::MH_DYLIB_STUB =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Blue)).set_fg(Some(Black)))?,
                header::MH_DSYM =>  fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(Green)).set_fg(Some(Black)))?,
                header::MH_CORE => fmt.set_color(::termcolor::ColorSpec::new().set_intense(true).set_bg(Some(White)).set_fg(Some(Black)))?,
                _ => ()
            }
            write!(fmt, "{}", kind_str)?;
            fmt.reset()
        };
        fmt_hdr(fmt, "Mach-o ")?;
        kind(fmt, &mach.header)?;
        write!(fmt, " ")?;
        fmt_name_bold(fmt, mach::constants::cputype::get_arch_name_from_types(machine,header.cpusubtype).unwrap_or("None"))?;
        write!(fmt, "-{} @ ", endianness)?;
        fmt_addrx(fmt, mach.entry as u64)?;
        writeln!(fmt, ":")?;
        writeln!(fmt, "")?;

        let lcs = &mach.load_commands;
        fmt_header(fmt, "LoadCommands", lcs.len())?;
        for (i, lc) in lcs.into_iter().enumerate() {
            fmt_idx(fmt, i)?;
            write!(fmt, " ")?;
            let name = load_command::cmd_to_str(lc.command.cmd());
            let name = &format!("{:.27}", name);
            match lc.command {
                load_command::CommandVariant::Segment32        (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Segment64        (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Symtab           (_command) => fmt_name_color(fmt, name, Yellow)?,
                load_command::CommandVariant::Dysymtab         (_command) => fmt_name_color(fmt, name, Green)?,
                load_command::CommandVariant::LoadDylinker     (_command) => fmt_name_color(fmt, name, Yellow)?,
                load_command::CommandVariant::LoadDylib        (_command)
                    | load_command::CommandVariant::LoadUpwardDylib(_command)
                    | load_command::CommandVariant::ReexportDylib  (_command)
                    | load_command::CommandVariant::LazyLoadDylib  (_command) => fmt_name_color(fmt, name, Blue)?,
                load_command::CommandVariant::DyldInfo         (_command)
                    | load_command::CommandVariant::DyldInfoOnly   (_command) => fmt_name_color(fmt, name, Cyan)?,
                load_command::CommandVariant::Unixthread       (_command) => fmt_name_color(fmt, name, Red)?,
                load_command::CommandVariant::Main             (_command) => fmt_name_color(fmt, name, Red)?,
                _ => fmt_name_bold(fmt, name)?
            }
            writeln!(fmt, "")?;
        }

        writeln!(fmt, "")?;

        let segments = &mach.segments;
        fmt_header(fmt, "Segments", segments.len())?;
        for (ref _i, ref segment) in segments.into_iter().enumerate() {
            let name = segment.name().unwrap();
            let sections = &segment.sections().unwrap();
            let mut segment_table = new_table(row![b->"Segment", b->"# Sections"]);
            segment_table.add_row(Row::new(vec![
                str_cell(&name),
                Cell::new(&sections.len().to_string()),
            ]));
            flush(fmt, &writer, segment_table, args.color)?;

            let mut section_table = new_table(row![b->"", b->"Idx", b->"Name", b->"Addr", b->"Size", b->"Offset", b->"Align", b->"Reloff", b->"Nrelocs", b->"Flags"]);
            for (i, &(ref section, _)) in sections.into_iter().enumerate() {
                if let Ok(name) = section.name() {
                    section_table.add_row(Row::new(vec![
                        Cell::new(&format!("{:4}", "")), // filler
                        Cell::new(&i.to_string()),
                        Cell::new(name).style_spec("Fyb"),
                        addrx_cell(section.addr),
                        sz_cell(section.size),
                        offsetx_cell(section.offset as u64),
                        Cell::new(&format!("{}", section.align)),
                        offsetx_cell(section.reloff as u64),
                        Cell::new(&format!("{}", section.nreloc)),
                        Cell::new(&format!("{:#x}", section.flags)),
                    ]));
                } else {
                    section_table.add_row(Row::new(vec![
                        Cell::new(&i.to_string()),
                        Cell::new("BAD SECTION NAME"),
                    ]));
                }
            }
            flush(fmt, &writer, section_table, args.color)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        let mut relocations: Vec<_> = Vec::new();
        let mut nrelocs = 0;
        let r = mach.relocations().unwrap();
        for (_i, relocs, section) in r.into_iter() {
            let section_name = section.name().unwrap();
            let segment_name = section.segname().unwrap();
            let mut rs = Vec::new();
            for reloc in relocs {
                let reloc = reloc.unwrap();
                nrelocs += 1;
                rs.push(reloc);
            }
            if !rs.is_empty() { relocations.push((segment_name.to_owned(), section_name.to_owned(), rs)) };
        }
        // need this to print relocation references
        let symbols = mach.symbols().collect::<Vec<_>>();
        let sections = mach.segments.sections().flat_map(|x| x).map(|s| s.unwrap().0).collect::<Vec<_>>();

        fmt_header(fmt, "Relocations", nrelocs)?;
        for (n1, n2, relocs) in relocations {
            let mut section_table = new_table(row![b->"Segment", b->"Section", b->"Count"]);
            section_table.add_row(Row::new(vec![
                str_cell(&n1),
                str_cell(&n2),
                Cell::new(&relocs.len().to_string()),
            ]));
            flush(fmt, &writer, section_table, args.color)?;
            let mut reloc_table = new_table(row![b->"", b->"Type", b->"Offset", b->"Length", b->"PIC", b->"Extern", b->"SymbolNum", b->"Symbol"]);
            for reloc in relocs {
                let idx = reloc.r_symbolnum();
                let name_cell = {
                    if reloc.is_extern() {
                        // FIXME: i cannot currently get this to compile without iterating and doing all this nonsense, otherwise move errors...
                        let mut maybe_name = None;
                        for (i, symbol) in symbols.iter().enumerate() {
                            match symbol {
                                &Ok((ref name, _)) => {
                                    let name: &str = name;
                                    if i == idx {
                                        maybe_name = Some(name);
                                    }
                                },
                                &Err(_) => ()
                            }
                        }
                        match maybe_name {
                            Some(name) => string_cell(&args, name),
                            None => {
                                cell("None").style_spec("b")
                            },
                        }
                    // not extern so the symbol num should reference a section
                    } else {
                        let section = &sections[idx - 1 as usize];
                        let sectname = section.name()?;
                        let segname = section.segname()?;
                        cell(format!("{}.{}", segname, sectname)).style_spec("bi")
                    }
                };
                reloc_table.add_row(Row::new(vec![
                    Cell::new(&format!("{:4}", "")),
                    cell(reloc.to_str(machine)),
                    addrx_cell(reloc.r_address as u64),
                    cell(reloc.r_length()),
                    bool_cell(reloc.is_pic()),
                    bool_cell(reloc.is_extern()),
                    offsetx_cell(idx as u64),
                    name_cell,
                ]));
            }

            flush(fmt, &writer, reloc_table, args.color)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        fmt_header(fmt, "Symbols", symbols.len())?;
        let mut symbol_table = new_table(row![b->"Offset", b->"Name", b->"Section", b->"Global", b->"Undefined"]);
        for (i, symbol) in symbols.into_iter().enumerate() {
            match symbol {
                Ok((name, symbol)) => {
                    let section_cell = if symbol.get_type() == mach::symbols::N_SECT {
                        // we subtract 1 because when N_SECT it is an ordinal, and hence indexing starts from 1
                        let section = &sections[symbol.n_sect - 1 as usize];
                        let sectname = section.name()?;
                        let segname = section.segname()?;
                        cell(format!("{}.{}", segname, sectname)).style_spec("b")
                    } else {
                        cell("None").style_spec("i")
                    };
                    symbol_table.add_row(Row::new(vec![
                        addrx_cell(symbol.n_value as u64),
                        string_cell(&args, name),
                        section_cell,
                        bool_cell(symbol.is_global()),
                        bool_cell(symbol.is_undefined()),
                    ]));
                },
                Err(e) => {
                    writeln!(fmt, "  {}: {}", i, e)?;
                }
            }
        }
        flush(fmt, &writer, symbol_table, args.color)?;
        writeln!(fmt)?;

        let fmt_exports = |fmt: &mut Buffer, name: &str, syms: &[Export] | -> io::Result<()> {
            fmt_header(fmt, name, syms.len())?;
            for sym in syms {
                fmt_addr_right(fmt, sym.offset)?;
                write!(fmt, " ")?;
                fmt_string(fmt, args, &sym.name)?;
                write!(fmt, " (")?;
                fmt_sz(fmt, sym.size as u64)?;
                writeln!(fmt, ")")?;
            }
            writeln!(fmt, "")
        };

        let exports = match mach.exports () { Ok(exports) => exports, Err(_) => Vec::new() };
        fmt_exports(fmt, "Exports", &exports)?;

        let imports = match mach.imports () { Ok(imports) => imports, Err(_) => Vec::new() };
        fmt_header(fmt, "Imports", imports.len())?;
        for sym in imports {
            fmt_addr_right(fmt, sym.offset)?;
            write!(fmt, " ")?;
            fmt_string(fmt, args, &sym.name)?;
            write!(fmt, " (")?;
            fmt_sz(fmt, sym.size as u64)?;
            write!(fmt, ")")?;
            write!(fmt, " -> ")?;
            fmt_lib(fmt, sym.dylib)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        fmt_header(fmt, "Libraries", mach.libs.len() - 1)?;
        for lib in &mach.libs[1..] {
            fmt_lib_right(fmt, lib)?;
            writeln!(fmt, "")?;
        }
        writeln!(fmt, "")?;

        write!(fmt, "Name: ")?;
        fmt_str_option(fmt, &mach.name)?;
        writeln!(fmt, "")?;
        write!(fmt, "is_64: ")?;
        fmt_bool(fmt, mach.header.container() == container::Container::Big)?;
        writeln!(fmt, "")?;
        write!(fmt, "is_lib: ")?;
        fmt_bool(fmt, mach.header.filetype == header::MH_DYLIB)?;
        writeln!(fmt, "")?;
        write!(fmt, "little_endian: ")?;
        fmt_bool(fmt, mach.header.is_little_endian())?;
        writeln!(fmt, "")?;
        write!(fmt, "entry: ")?;
        fmt_addr(fmt, mach.entry as u64)?;
        writeln!(fmt, "")?;

        writer.print(&fmt)?;
        Ok(())
    }
}
<|endoftext|>