<commit_before>pub struct Document {
    pub id: i32,
    pub url: String,
    pub title: String,
    pub content: String,
}

impl Document {
    pub fn new(id: i32, url: String, title: String, content: String) -> Document {
        Document {
            id: id,
            url: url,
            title: title,
            content: content,
        }
    }
}

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

    #[test]
    fn test_new_document() {
        let d = Document::new(
            1,
            "http://acme.com/about".to_string(),
            "About Us".to_string(),
            "We are Acme and we make things.".to_string(),
        );

        assert_eq!(1, d.id);
        assert_eq!("http://acme.com/about", d.url);
        assert_eq!("About Us", d.title);
        assert_eq!("We are Acme and we make things.", d.content);
    }
}<commit_msg>Added documentation and Index model<commit_after>//! The models crate handles instantiating and using documents and indexes.

/// Document contains information pertaining to a document such as a webpage.
/// Documents have many indexes.
pub struct Document {
    pub id: i32,
    pub url: String,
    pub title: String,
    pub content: String,
}

impl Document {
    /// new creates a new instantiation of a Document.
    ///
    /// # Examples
    ///
    /// new Document
    ///
    /// ```
    /// let d = Document::new(
    ///     1,
    ///     "http://acme.com/about".to_string(),
    ///     "About Us".to_string(),
    ///     "We are Acme and we make things.".to_string(),
    /// );
    pub fn new(id: i32, url: String, title: String, content: String) -> Document {
        Document {
            id: id,
            url: url,
            title: title,
            content: content,
        }
    }
}

/// Index contains information about a particular word in a document. A document
/// can only contain unique indexes. The locations field should hold all
/// occurences of the word in the document and their location in the document.
pub struct Index {
    pub id: i32,
    pub word: String,
    pub locations: Vec<i32>,
}

impl Index {
    /// new creates a new instantiation of an Index.
    ///
    /// # Examples
    ///
    /// new Index
    ///
    /// ```
    /// let i = Index::new(
    ///     1,
    ///     "acme".to_string(),
    ///     vec![3, 15, 72],
    /// );
    pub fn new(id: i32, word: String, locations: Vec<i32>) -> Index {
        Index {
            id: id,
            word: word,
            locations: locations,
        }
    }
}

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

    #[test]
    fn test_new_document() {
        let d = Document::new(
            1,
            "http://acme.com/about".to_string(),
            "About Us".to_string(),
            "We are Acme and we make things.".to_string(),
        );

        assert_eq!(1, d.id);
        assert_eq!("http://acme.com/about", d.url);
        assert_eq!("About Us", d.title);
        assert_eq!("We are Acme and we make things.", d.content);
    }

    #[test]
    fn test_new_index() {
        let i = Index::new(
            1,
            "acme".to_string(),
            vec![3, 15, 72],
        );

        assert_eq!(1, i.id);
        assert_eq!("acme", i.word);
        assert_eq!(3, i.locations.len());
    }
}<|endoftext|>


<commit_before>use traits::{Cipher, U8Array};

/// A `CipherState` can encrypt and decrypt data.
///
/// Mostly like `CipherState` in the spec, but must be created with a key.
///
/// # Panics
///
/// Encryption and decryption methods will panic if nonce reaches maximum u64, i.e., 2 ^ 64 - 1.
pub struct CipherState<C: Cipher> {
    key: C::Key,
    n: u64,
}

impl<C> Clone for CipherState<C>
    where C: Cipher
{
    fn clone(&self) -> Self {
        Self {
            key: self.key.clone(),
            n: self.n,
        }
    }
}

impl<C> CipherState<C>
    where C: Cipher
{
    /// Name of cipher, e.g. “ChaChaPoly”.
    pub fn name() -> &'static str {
        C::name()
    }

    /// Create a new `CipherState` with a `key` and a nonce `n`.
    pub fn new(key: &[u8], n: u64) -> Self {
        CipherState {
            key: C::Key::from_slice(key),
            n: n,
        }
    }

    /// AEAD encryption.
    pub fn encrypt_ad(&mut self, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) {
        C::encrypt(&self.key, self.n, authtext, plaintext, out);
        // This will fail when n == 2 ^ 64 - 1, complying to the spec.
        self.n = self.n.checked_add(1).unwrap();
    }

    /// AEAD decryption.
    pub fn decrypt_ad(&mut self,
                      authtext: &[u8],
                      ciphertext: &[u8],
                      out: &mut [u8])
                      -> Result<(), ()> {
        C::decrypt(&self.key, self.n, authtext, ciphertext, out)?;
        self.n = self.n.checked_add(1).unwrap();
        Ok(())
    }

    /// Encryption.
    pub fn encrypt(&mut self, plaintext: &[u8], out: &mut [u8]) {
        self.encrypt_ad(&[0u8; 0], plaintext, out)
    }

    /// Encryption, returns ciphertext as `Vec<u8>`.
    #[cfg(feature = "use_std")]
    pub fn encrypt_vec(&mut self, plaintext: &[u8]) -> Vec<u8> {
        let mut out = vec![0u8; plaintext.len() + 16];
        self.encrypt(plaintext, &mut out);
        out
    }

    /// Decryption.
    pub fn decrypt(&mut self, ciphertext: &[u8], out: &mut [u8]) -> Result<(), ()> {
        self.decrypt_ad(&[0u8; 0], ciphertext, out)
    }

    /// Decryption, returns plaintext as `Vec<u8>`.
    #[cfg(feature = "use_std")]
    pub fn decrypt_vec(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, ()> {
        let mut out = vec![0u8; ciphertext.len() - 16];
        self.decrypt(ciphertext, &mut out)?;
        Ok(out)
    }

    /// Get the next value of `n`. Could be used to decide on whether to re-key, etc.
    pub fn get_next_n(&self) -> u64 {
        self.n
    }

    /// Get underlying cipher and nonce.
    ///
    /// This is useful for e.g. WireGuard. Because packets may be lost or arrive out of order,
    /// they would likely want to deal with nonces themselves.
    pub fn extract(self) -> (C::Key, u64) {
        (self.key, self.n)
    }
}
<commit_msg>CipherState::decrypt_vec(): check ciphertext length<commit_after>use traits::{Cipher, U8Array};

/// A `CipherState` can encrypt and decrypt data.
///
/// Mostly like `CipherState` in the spec, but must be created with a key.
///
/// # Panics
///
/// Encryption and decryption methods will panic if nonce reaches maximum u64, i.e., 2 ^ 64 - 1.
pub struct CipherState<C: Cipher> {
    key: C::Key,
    n: u64,
}

impl<C> Clone for CipherState<C>
    where C: Cipher
{
    fn clone(&self) -> Self {
        Self {
            key: self.key.clone(),
            n: self.n,
        }
    }
}

impl<C> CipherState<C>
    where C: Cipher
{
    /// Name of cipher, e.g. “ChaChaPoly”.
    pub fn name() -> &'static str {
        C::name()
    }

    /// Create a new `CipherState` with a `key` and a nonce `n`.
    pub fn new(key: &[u8], n: u64) -> Self {
        CipherState {
            key: C::Key::from_slice(key),
            n: n,
        }
    }

    /// AEAD encryption.
    pub fn encrypt_ad(&mut self, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) {
        C::encrypt(&self.key, self.n, authtext, plaintext, out);
        // This will fail when n == 2 ^ 64 - 1, complying to the spec.
        self.n = self.n.checked_add(1).unwrap();
    }

    /// AEAD decryption.
    pub fn decrypt_ad(&mut self,
                      authtext: &[u8],
                      ciphertext: &[u8],
                      out: &mut [u8])
                      -> Result<(), ()> {
        C::decrypt(&self.key, self.n, authtext, ciphertext, out)?;
        self.n = self.n.checked_add(1).unwrap();
        Ok(())
    }

    /// Encryption.
    pub fn encrypt(&mut self, plaintext: &[u8], out: &mut [u8]) {
        self.encrypt_ad(&[0u8; 0], plaintext, out)
    }

    /// Encryption, returns ciphertext as `Vec<u8>`.
    #[cfg(feature = "use_std")]
    pub fn encrypt_vec(&mut self, plaintext: &[u8]) -> Vec<u8> {
        let mut out = vec![0u8; plaintext.len() + 16];
        self.encrypt(plaintext, &mut out);
        out
    }

    /// Decryption.
    pub fn decrypt(&mut self, ciphertext: &[u8], out: &mut [u8]) -> Result<(), ()> {
        self.decrypt_ad(&[0u8; 0], ciphertext, out)
    }

    /// Decryption, returns plaintext as `Vec<u8>`.
    #[cfg(feature = "use_std")]
    pub fn decrypt_vec(&mut self, ciphertext: &[u8]) -> Result<Vec<u8>, ()> {
        if ciphertext.len() < 16 {
            return Err(());
        }
        let mut out = vec![0u8; ciphertext.len() - 16];
        self.decrypt(ciphertext, &mut out)?;
        Ok(out)
    }

    /// Get the next value of `n`. Could be used to decide on whether to re-key, etc.
    pub fn get_next_n(&self) -> u64 {
        self.n
    }

    /// Get underlying cipher and nonce.
    ///
    /// This is useful for e.g. WireGuard. Because packets may be lost or arrive out of order,
    /// they would likely want to deal with nonces themselves.
    pub fn extract(self) -> (C::Key, u64) {
        (self.key, self.n)
    }
}
<|endoftext|>


<commit_before>//! The `parser` module contains structures and functions for parsing the VQA
//! (Vector Quantized Animation) format.
//!
//! Information on the VQA format found at:
//!
//! * https://wiki.multimedia.cx/index.php/VQA#Technical_Description
//! * https://multimedia.cx/HC-VQA.TXT

use nom::{be_u32, le_u8, le_u16, le_u32, IResult};

#[derive(Debug)]
pub struct FormChunk {
    pub size: u32
}

named!(pub form_chunk<FormChunk>,
    do_parse!(
        tag!("FORM") >>
        size: be_u32 >>
        (FormChunk {
            size: size
        })
    )
);

#[derive(Debug)]
pub enum VQAVersion {
    One,
    Two,
    Three
}

named!(pub vqa_version<VQAVersion>,
    switch!(le_u16,
        1 => value!(VQAVersion::One) |
        2 => value!(VQAVersion::Two) |
        3 => value!(VQAVersion::Three)
    )
);

bitflags! {
    pub struct VQAFlags: u16 {
        const HAS_SOUND = 0b00000001;
    }
}

#[derive(Debug)]
pub struct VQAHeader {
    pub version       : VQAVersion, // VQA version number
    pub flags         : VQAFlags,   // VQA flags
    pub num_frames    : u16,        // Number of frames
    pub width         : u16,        // Movie width (pixels)
    pub height        : u16,        // Movie height (pixels)
    pub block_width   : u8,         // Width of each image block (pixels)
    pub block_height  : u8,         // Height of each image block (pixels)
    pub frame_rate    : u8,         // Frame rate of the VQA
    pub cbparts       : u8,         // How many images use the same lookup table
    pub colors        : u16,        // Max number of colors used in VQA
    pub maxblocks     : u16,        // Max number of image blocks
    pub unk1          : u32,        // Always 0?
    pub unk2          : u16,        // Some kind of size?
    pub freq          : u16,        // Sound sampling frequency
    pub channels      : u8,         // Number of sound channels
    pub bits          : u8,         // Sound resolution
    pub unk3          : u32,        // Always 0?
    pub unk4          : u16,        // 0 in old VQAs, 4 in HiColor VQAs?
    pub max_cbfz_size : u32,        // 0 in old VQAs, CBFZ size in HiColor
    pub unk5          : u32,        // Always 0?
}

named!(pub vqa_header<VQAHeader>,
    do_parse!(
        tag!("VQHD")              >>
        tag!(b"\x00\x00\x00\x2a") >> // VQAHeader is always 42 bytes long
        version: vqa_version      >>
        flags: le_u16             >>
        num_frames: le_u16        >>
        width: le_u16             >>
        height: le_u16            >>
        block_width: le_u8        >>
        block_height: le_u8       >>
        frame_rate: le_u8         >>
        cbparts: le_u8            >>
        colors: le_u16            >>
        maxblocks: le_u16         >>
        unk1: le_u32              >>
        unk2: le_u16              >>
        freq: le_u16              >>
        channels: le_u8           >>
        bits: le_u8               >>
        unk3: le_u32              >>
        unk4: le_u16              >>
        max_cbfz_size: le_u32     >>
        unk5: le_u32              >>
        (VQAHeader {
            version: version,
            flags: VQAFlags::from_bits_truncate(flags),
            num_frames: num_frames,
            width: width,
            height: height,
            block_width: block_width,
            block_height: block_height,
            frame_rate: frame_rate,
            cbparts: cbparts,
            colors: colors,
            maxblocks: maxblocks,
            unk1: unk1,
            unk2: unk2,
            freq: freq,
            channels: channels,
            bits: bits,
            unk3: unk3,
            unk4: unk4,
            max_cbfz_size: max_cbfz_size,
            unk5: unk5
        })
    )
);<commit_msg>add finf_chunk parser<commit_after>//! The `parser` module contains structures and functions for parsing the VQA
//! (Vector Quantized Animation) format.
//!
//! Information on the VQA format found at:
//!
//! * https://wiki.multimedia.cx/index.php/VQA#Technical_Description
//! * https://multimedia.cx/HC-VQA.TXT

use nom::{be_u32, le_u8, le_u16, le_u32, IResult};

#[derive(Debug)]
pub struct FormChunk {
    pub size: u32
}

named!(pub form_chunk<FormChunk>,
    do_parse!(
        tag!("FORM") >>
        size: be_u32 >>
        (FormChunk {
            size: size
        })
    )
);

#[derive(Debug)]
pub enum VQAVersion {
    One,
    Two,
    Three
}

named!(pub vqa_version<VQAVersion>,
    switch!(le_u16,
        1 => value!(VQAVersion::One) |
        2 => value!(VQAVersion::Two) |
        3 => value!(VQAVersion::Three)
    )
);

bitflags! {
    pub struct VQAFlags: u16 {
        const HAS_SOUND = 0b00000001;
    }
}

#[derive(Debug)]
pub struct VQAHeader {
    pub version       : VQAVersion, // VQA version number
    pub flags         : VQAFlags,   // VQA flags
    pub num_frames    : u16,        // Number of frames
    pub width         : u16,        // Movie width (pixels)
    pub height        : u16,        // Movie height (pixels)
    pub block_width   : u8,         // Width of each image block (pixels)
    pub block_height  : u8,         // Height of each image block (pixels)
    pub frame_rate    : u8,         // Frame rate of the VQA
    pub cbparts       : u8,         // How many images use the same lookup table
    pub colors        : u16,        // Max number of colors used in VQA
    pub maxblocks     : u16,        // Max number of image blocks
    pub unk1          : u32,        // Always 0?
    pub unk2          : u16,        // Some kind of size?
    pub freq          : u16,        // Sound sampling frequency
    pub channels      : u8,         // Number of sound channels
    pub bits          : u8,         // Sound resolution
    pub unk3          : u32,        // Always 0?
    pub unk4          : u16,        // 0 in old VQAs, 4 in HiColor VQAs?
    pub max_cbfz_size : u32,        // 0 in old VQAs, CBFZ size in HiColor
    pub unk5          : u32,        // Always 0?
}

named!(pub vqa_header<VQAHeader>,
    do_parse!(
        tag!("VQHD")              >>
        tag!(b"\x00\x00\x00\x2a") >> // VQAHeader is always 42 bytes long
        version: vqa_version      >>
        flags: le_u16             >>
        num_frames: le_u16        >>
        width: le_u16             >>
        height: le_u16            >>
        block_width: le_u8        >>
        block_height: le_u8       >>
        frame_rate: le_u8         >>
        cbparts: le_u8            >>
        colors: le_u16            >>
        maxblocks: le_u16         >>
        unk1: le_u32              >>
        unk2: le_u16              >>
        freq: le_u16              >>
        channels: le_u8           >>
        bits: le_u8               >>
        unk3: le_u32              >>
        unk4: le_u16              >>
        max_cbfz_size: le_u32     >>
        unk5: le_u32              >>
        (VQAHeader {
            version: version,
            flags: VQAFlags::from_bits_truncate(flags),
            num_frames: num_frames,
            width: width,
            height: height,
            block_width: block_width,
            block_height: block_height,
            frame_rate: frame_rate,
            cbparts: cbparts,
            colors: colors,
            maxblocks: maxblocks,
            unk1: unk1,
            unk2: unk2,
            freq: freq,
            channels: channels,
            bits: bits,
            unk3: unk3,
            unk4: unk4,
            max_cbfz_size: max_cbfz_size,
            unk5: unk5
        })
    )
);

#[derive(Debug)]
pub struct FINFChunk {
    pub size: u32,
    pub offsets: Vec<u32>
}

named!(pub finf_chunk<FINFChunk>,
    do_parse!(
        tag!("FINF") >>
        size: be_u32 >>
        offsets: many_m_n!(size as usize / 4, size as usize / 4, le_u32) >>
        (FINFChunk {
            size: size,
            offsets: offsets
        })
    )
);<|endoftext|>


<commit_before>use std::str::from_utf8;
use std::result::Result;
use nom::*;

/*
 * Core structs
 */

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: &'a [u8]
}

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     &'a str,
    pub mode:     &'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   &'a str,
    pub typeflag: TypeFlag,
    pub linkname: &'a str,
    pub ustar:    ExtraHeader<'a>
}

/* TODO: support vendor specific */
#[derive(Debug,PartialEq,Eq)]
pub enum TypeFlag {
    NormalFile,
    HardLink,
    SymbolicLink,
    CharacterSpecial,
    BlockSpecial,
    Directory,
    FIFO,
    ContiguousFile,
    PaxInterexchangeFormat,
    PaxExtendedAttributes,
    VendorSpecific
}

#[derive(Debug,PartialEq,Eq)]
pub enum ExtraHeader<'a> {
    UStar(UStarHeader<'a>),
    Padding
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    &'a str,
    pub version:  &'a str,
    pub uname:    &'a str,
    pub gname:    &'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub extra:    UStarExtraHeader<'a>
}

#[derive(Debug,PartialEq,Eq)]
pub enum UStarExtraHeader<'a> {
    PosixUStar(PosixUStarHeader<'a>),
    Pax(PaxHeader<'a>)
}

#[derive(Debug,PartialEq,Eq)]
pub struct PosixUStarHeader<'a> {
    pub prefix: &'a str
}

#[derive(Debug,PartialEq,Eq)]
pub struct PaxHeader<'a> {
    pub atime:      u64,
    pub ctime:      u64,
    pub offset:     u64,
    pub longnames:  &'a str,
    pub sparses:    Vec<Sparse>,
    pub isextended: bool,
    pub realsize:   u64,
}

#[derive(Debug,PartialEq,Eq)]
pub struct Sparse {
    pub offset:   u64,
    pub numbytes: u64
}

#[derive(Debug,PartialEq,Eq)]
pub struct Padding;

/*
 * Useful macros
 */

macro_rules! take_str_eat_garbage (
    ( $i:expr, $size:expr ) => (
        chain!($i,
            s: map_res!(take_until!("\0"), from_utf8)                  ~
            length: expr_opt!({($size as usize).checked_sub(s.len())}) ~
            take!(length),
            ||{
                s
            }
        )
    );
);

named!(parse_str4<&[u8], &str>, take_str_eat_garbage!(4));
named!(parse_str8<&[u8], &str>, take_str_eat_garbage!(8));
named!(parse_str32<&[u8], &str>, take_str_eat_garbage!(32));
named!(parse_str100<&[u8], &str>, take_str_eat_garbage!(100));
named!(parse_str155<&[u8], &str>, take_str_eat_garbage!(155));

macro_rules! take_until_expr_with_limit_consume(
  ($i:expr, $submac:ident!( $($args:tt)* ), $stop: expr, $limit: expr) => (
    {
      let mut begin = 0;
      let mut remaining = $i.len();
      let mut res = Vec::new();
      let mut cnt = 0;
      let mut err = false;
      let mut append = true;
      loop {
        match $submac!(&$i[begin..], $($args)*) {
          IResult::Done(i,o) => {
            if append {
              if $stop(&o) {
                append = false;
              } else {
                res.push(o);
              }
            }
            begin += remaining - i.len();
            remaining = i.len();
            cnt = cnt + 1;
            if cnt == $limit {
              break
            }
          },
          IResult::Error(_)  => {
            err = true;
            break;
          },
          IResult::Incomplete(_) => {
            break;
          }
        }
      }
      if err {
        IResult::Error(Err::Position(ErrorKind::TakeUntil,$i))
      } else if cnt == $limit {
        IResult::Done(&$i[begin..], res)
      } else {
        IResult::Incomplete(Needed::Unknown)
      }
    }
  );
  ($i:expr, $f:expr, $stop: expr, $limit: expr) => (
    take_until_expr_with_limit_consume!($i, call!($f), $stop, $limit);
  );
);

/*
 * Octal string parsing
 */

pub fn octal_to_u64(s: &str) -> Result<u64, &'static str> {
    let mut u = 0;

    for c in s.chars() {
        if c < '0' || c > '7' {
            return Err("invalid octal string received");
        }
        u *= 8;
        u += (c as u64) - ('0' as u64);
    }

    Ok(u)
}

fn parse_octal(i: &[u8], n: usize) -> IResult<&[u8], u64> {
    map_res!(i, take_str_eat_garbage!(n), octal_to_u64)
}

named!(parse_octal8<&[u8], u64>, apply!(parse_octal, 8));
named!(parse_octal12<&[u8], u64>, apply!(parse_octal, 12));

/*
 * TypeFlag parsing
 */

fn char_to_type_flag(c: char) -> TypeFlag {
    match c {
        '0' | '\0' => TypeFlag::NormalFile,
        '1' => TypeFlag::HardLink,
        '2' => TypeFlag::SymbolicLink,
        '3' => TypeFlag::CharacterSpecial,
        '4' => TypeFlag::BlockSpecial,
        '5' => TypeFlag::Directory,
        '6' => TypeFlag::FIFO,
        '7' => TypeFlag::ContiguousFile,
        'g' => TypeFlag::PaxInterexchangeFormat,
        'x' => TypeFlag::PaxExtendedAttributes,
        'A' ... 'Z' => TypeFlag::VendorSpecific,
        _ => TypeFlag::NormalFile
    }
}

fn bytes_to_type_flag(i: &[u8]) -> TypeFlag {
    char_to_type_flag(i[0] as char)
}

named!(parse_type_flag<&[u8], TypeFlag>, map!(take!(1), bytes_to_type_flag));

/*
 * Sparse parsing
 */

named!(parse_one_sparse<&[u8], Sparse>, map!(pair!(parse_octal12, parse_octal12), |(offset, numbytes)| Sparse { offset: offset, numbytes: numbytes }));

fn parse_sparses_with_limit(i: &[u8], limit: usize) -> IResult<&[u8], Vec<Sparse>> {
    take_until_expr_with_limit_consume!(i, parse_one_sparse, |s: &Sparse| s.offset == 0 && s.numbytes == 0, limit)
}

fn add_to_vec<'a, 'b>(sparses: &'a mut Vec<Sparse>, extra: &'b mut Vec<Sparse>) -> &'a mut Vec<Sparse> {
    while sparses.len() != 0 {
      extra.pop().map(|s| sparses.push(s));
    }
    sparses
}

fn parse_extra_sparses<'a, 'b>(i: &'a [u8], isextended: bool, sparses: &'b mut Vec<Sparse>) -> IResult<&'a [u8], &'b mut Vec<Sparse>> {
    if isextended {
        chain!(i,
            mut sps:       apply!(parse_sparses_with_limit, 21) ~
            extended:      parse_bool                     ~
            take!(7) /* padding to 512 */                 ~
            extra_sparses: apply!(parse_extra_sparses, extended, add_to_vec(sparses, &mut sps)),
            ||{
                extra_sparses
            }
        )
    } else {
        IResult::Done(i, sparses)
    }
}

fn parse_pax_extra_sparses<'a, 'b>(i: &'a [u8], h: &'b mut PaxHeader) -> IResult<&'a [u8], &'b mut Vec<Sparse>> {
    parse_extra_sparses(i, h.isextended, &mut h.sparses)
}

/*
 * Boolean parsing
 */

fn to_bool(i: &[u8]) -> bool {
    i[0] != 0
}

named!(parse_bool<&[u8], bool>, map!(take!(1), to_bool));

/*
 * UStar PAX extended parsing
 */

fn parse_ustar00_extra_pax(i: &[u8]) -> IResult<&[u8], PaxHeader> {
    chain!(i,
        atime:       parse_octal12                       ~
        ctime:       parse_octal12                       ~
        offset:      parse_octal12                       ~
        longnames:   parse_str4                          ~
        take!(1)                                         ~
        sparses:     apply!(parse_sparses_with_limit, 4) ~
        isextended:  parse_bool                          ~
        realsize:    parse_octal12                       ~
        take!(17), /* padding to 512 */
        ||{
            PaxHeader {
                atime:         atime,
                ctime:         ctime,
                offset:        offset,
                longnames:     longnames,
                sparses:       sparses,
                isextended:    isextended,
                realsize:      realsize,
            }
        }
    )
}

/*
 * UStar Posix parsing
 */

named!(parse_ustar00_extra_posix<&[u8], UStarExtraHeader>, map!(pair!(parse_str155, take!(12)), |(prefix, _)| UStarExtraHeader::PosixUStar(PosixUStarHeader { prefix: prefix })));

fn parse_ustar00_extra<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], UStarExtraHeader<'a>> {
    match *flag {
        TypeFlag::PaxInterexchangeFormat => {
            chain!(i,
                mut header: parse_ustar00_extra_pax ~
                apply!(parse_pax_extra_sparses, &mut header),
                ||{
                    UStarExtraHeader::Pax(header)
                }
            )
        },
        _ => parse_ustar00_extra_posix(i)
    }
}

fn parse_ustar00<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], ExtraHeader<'a>> {
    chain!(i,
        tag!("00")             ~
        uname:    parse_str32  ~
        gname:    parse_str32  ~
        devmajor: parse_octal8 ~
        devminor: parse_octal8 ~
        extra:    apply!(parse_ustar00_extra, flag),
        ||{
            ExtraHeader::UStar(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: devmajor,
                devminor: devminor,
                extra:    extra
            })
        }
    )
}

fn parse_ustar<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], ExtraHeader<'a>> {
    map!(i, pair!(tag!("ustar\0"), apply!(parse_ustar00, flag)), |(_, ustar)| ustar)
}

/*
 * Posix tar archive header parsing
 */

fn parse_posix(i: &[u8]) -> IResult<&[u8], ExtraHeader> {
    map!(i, take!(255), |_| ExtraHeader::Padding) /* padding to 512 */
}

fn parse_header(i: &[u8]) -> IResult<&[u8], PosixHeader> {
    chain!(i,
        name:     parse_str100    ~
        mode:     parse_str8      ~
        uid:      parse_octal8    ~
        gid:      parse_octal8    ~
        size:     parse_octal12   ~
        mtime:    parse_octal12   ~
        chksum:   parse_str8      ~
        typeflag: parse_type_flag ~
        linkname: parse_str100    ~
        ustar:    alt!(apply!(parse_ustar, &typeflag) | parse_posix),
        ||{
            PosixHeader {
                name:     name,
                mode:     mode,
                uid:      uid,
                gid:      gid,
                size:     size,
                mtime:    mtime,
                chksum:   chksum,
                typeflag: typeflag,
                linkname: linkname,
                ustar:    ustar
            }
        }
    )
}

/*
 * Contents parsing
 */

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &[u8]> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    map!(i, pair!(take!(size as usize), take!(padding as usize)), |(contents, _)| contents)
}

/*
 * Tar entry header + contents parsing
 */

fn parse_entry(i: &[u8]) -> IResult<&[u8], TarEntry> {
    chain!(i,
        header:   parse_header ~
        contents: apply!(parse_contents, header.size),
        ||{
            TarEntry {
                header: header,
                contents: contents
            }
        }
    )
}

/*
 * Tar archive parsing
 */

fn filter_entries(entries: Vec<TarEntry>) -> Vec<TarEntry> {
    /* Filter out empty entries */
    entries.into_iter().filter(|e| e.header.name != "").collect::<Vec<TarEntry>>()
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    map!(i, pair!(map!(many0!(parse_entry), filter_entries), eof), |(entries, _)| entries)
}

/*
 * Tests
 */

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;
    use nom::IResult;

    #[test]
    fn octal_to_u64_ok_test() {
        assert_eq!(octal_to_u64("756"), Ok(494));
        assert_eq!(octal_to_u64(""), Ok(0));
    }

    #[test]
    fn octal_to_u64_error_test() {
        assert_eq!(octal_to_u64("1238"), Err("invalid octal string received"));
        assert_eq!(octal_to_u64("a"), Err("invalid octal string received"));
        assert_eq!(octal_to_u64("A"), Err("invalid octal string received"));
    }

    #[test]
    fn take_str_eat_garbage_test() {
        let s = b"foobar\0\0\0\0baz";
        let baz = b"baz";
        assert_eq!(take_str_eat_garbage!(&s[..], 10), IResult::Done(&baz[..], "foobar"));
    }
}
<commit_msg>use named! where possible<commit_after>use std::str::from_utf8;
use std::result::Result;
use nom::*;

/*
 * Core structs
 */

#[derive(Debug,PartialEq,Eq)]
pub struct TarEntry<'a> {
    pub header:   PosixHeader<'a>,
    pub contents: &'a [u8]
}

#[derive(Debug,PartialEq,Eq)]
pub struct PosixHeader<'a> {
    pub name:     &'a str,
    pub mode:     &'a str,
    pub uid:      u64,
    pub gid:      u64,
    pub size:     u64,
    pub mtime:    u64,
    pub chksum:   &'a str,
    pub typeflag: TypeFlag,
    pub linkname: &'a str,
    pub ustar:    ExtraHeader<'a>
}

/* TODO: support vendor specific */
#[derive(Debug,PartialEq,Eq)]
pub enum TypeFlag {
    NormalFile,
    HardLink,
    SymbolicLink,
    CharacterSpecial,
    BlockSpecial,
    Directory,
    FIFO,
    ContiguousFile,
    PaxInterexchangeFormat,
    PaxExtendedAttributes,
    VendorSpecific
}

#[derive(Debug,PartialEq,Eq)]
pub enum ExtraHeader<'a> {
    UStar(UStarHeader<'a>),
    Padding
}

#[derive(Debug,PartialEq,Eq)]
pub struct UStarHeader<'a> {
    pub magic:    &'a str,
    pub version:  &'a str,
    pub uname:    &'a str,
    pub gname:    &'a str,
    pub devmajor: u64,
    pub devminor: u64,
    pub extra:    UStarExtraHeader<'a>
}

#[derive(Debug,PartialEq,Eq)]
pub enum UStarExtraHeader<'a> {
    PosixUStar(PosixUStarHeader<'a>),
    Pax(PaxHeader<'a>)
}

#[derive(Debug,PartialEq,Eq)]
pub struct PosixUStarHeader<'a> {
    pub prefix: &'a str
}

#[derive(Debug,PartialEq,Eq)]
pub struct PaxHeader<'a> {
    pub atime:      u64,
    pub ctime:      u64,
    pub offset:     u64,
    pub longnames:  &'a str,
    pub sparses:    Vec<Sparse>,
    pub isextended: bool,
    pub realsize:   u64,
}

#[derive(Debug,PartialEq,Eq)]
pub struct Sparse {
    pub offset:   u64,
    pub numbytes: u64
}

#[derive(Debug,PartialEq,Eq)]
pub struct Padding;

/*
 * Useful macros
 */

macro_rules! take_str_eat_garbage (
    ( $i:expr, $size:expr ) => (
        chain!($i,
            s: map_res!(take_until!("\0"), from_utf8)                  ~
            length: expr_opt!({($size as usize).checked_sub(s.len())}) ~
            take!(length),
            ||{
                s
            }
        )
    );
);

named!(parse_str4<&[u8], &str>, take_str_eat_garbage!(4));
named!(parse_str8<&[u8], &str>, take_str_eat_garbage!(8));
named!(parse_str32<&[u8], &str>, take_str_eat_garbage!(32));
named!(parse_str100<&[u8], &str>, take_str_eat_garbage!(100));
named!(parse_str155<&[u8], &str>, take_str_eat_garbage!(155));

macro_rules! take_until_expr_with_limit_consume(
  ($i:expr, $submac:ident!( $($args:tt)* ), $stop: expr, $limit: expr) => (
    {
      let mut begin = 0;
      let mut remaining = $i.len();
      let mut res = Vec::new();
      let mut cnt = 0;
      let mut err = false;
      let mut append = true;
      loop {
        match $submac!(&$i[begin..], $($args)*) {
          IResult::Done(i,o) => {
            if append {
              if $stop(&o) {
                append = false;
              } else {
                res.push(o);
              }
            }
            begin += remaining - i.len();
            remaining = i.len();
            cnt = cnt + 1;
            if cnt == $limit {
              break
            }
          },
          IResult::Error(_)  => {
            err = true;
            break;
          },
          IResult::Incomplete(_) => {
            break;
          }
        }
      }
      if err {
        IResult::Error(Err::Position(ErrorKind::TakeUntil,$i))
      } else if cnt == $limit {
        IResult::Done(&$i[begin..], res)
      } else {
        IResult::Incomplete(Needed::Unknown)
      }
    }
  );
  ($i:expr, $f:expr, $stop: expr, $limit: expr) => (
    take_until_expr_with_limit_consume!($i, call!($f), $stop, $limit);
  );
);

/*
 * Octal string parsing
 */

pub fn octal_to_u64(s: &str) -> Result<u64, &'static str> {
    let mut u = 0;

    for c in s.chars() {
        if c < '0' || c > '7' {
            return Err("invalid octal string received");
        }
        u *= 8;
        u += (c as u64) - ('0' as u64);
    }

    Ok(u)
}

fn parse_octal(i: &[u8], n: usize) -> IResult<&[u8], u64> {
    map_res!(i, take_str_eat_garbage!(n), octal_to_u64)
}

named!(parse_octal8<&[u8], u64>, apply!(parse_octal, 8));
named!(parse_octal12<&[u8], u64>, apply!(parse_octal, 12));

/*
 * TypeFlag parsing
 */

fn char_to_type_flag(c: char) -> TypeFlag {
    match c {
        '0' | '\0' => TypeFlag::NormalFile,
        '1' => TypeFlag::HardLink,
        '2' => TypeFlag::SymbolicLink,
        '3' => TypeFlag::CharacterSpecial,
        '4' => TypeFlag::BlockSpecial,
        '5' => TypeFlag::Directory,
        '6' => TypeFlag::FIFO,
        '7' => TypeFlag::ContiguousFile,
        'g' => TypeFlag::PaxInterexchangeFormat,
        'x' => TypeFlag::PaxExtendedAttributes,
        'A' ... 'Z' => TypeFlag::VendorSpecific,
        _ => TypeFlag::NormalFile
    }
}

fn bytes_to_type_flag(i: &[u8]) -> TypeFlag {
    char_to_type_flag(i[0] as char)
}

named!(parse_type_flag<&[u8], TypeFlag>, map!(take!(1), bytes_to_type_flag));

/*
 * Sparse parsing
 */

named!(parse_one_sparse<&[u8], Sparse>, map!(pair!(parse_octal12, parse_octal12), |(offset, numbytes)| Sparse { offset: offset, numbytes: numbytes }));

fn parse_sparses_with_limit(i: &[u8], limit: usize) -> IResult<&[u8], Vec<Sparse>> {
    take_until_expr_with_limit_consume!(i, parse_one_sparse, |s: &Sparse| s.offset == 0 && s.numbytes == 0, limit)
}

fn add_to_vec<'a, 'b>(sparses: &'a mut Vec<Sparse>, extra: &'b mut Vec<Sparse>) -> &'a mut Vec<Sparse> {
    while sparses.len() != 0 {
      extra.pop().map(|s| sparses.push(s));
    }
    sparses
}

fn parse_extra_sparses<'a, 'b>(i: &'a [u8], isextended: bool, sparses: &'b mut Vec<Sparse>) -> IResult<&'a [u8], &'b mut Vec<Sparse>> {
    if isextended {
        chain!(i,
            mut sps:       apply!(parse_sparses_with_limit, 21) ~
            extended:      parse_bool                     ~
            take!(7) /* padding to 512 */                 ~
            extra_sparses: apply!(parse_extra_sparses, extended, add_to_vec(sparses, &mut sps)),
            ||{
                extra_sparses
            }
        )
    } else {
        IResult::Done(i, sparses)
    }
}

fn parse_pax_extra_sparses<'a, 'b>(i: &'a [u8], h: &'b mut PaxHeader) -> IResult<&'a [u8], &'b mut Vec<Sparse>> {
    parse_extra_sparses(i, h.isextended, &mut h.sparses)
}

/*
 * Boolean parsing
 */

fn to_bool(i: &[u8]) -> bool {
    i[0] != 0
}

named!(parse_bool<&[u8], bool>, map!(take!(1), to_bool));

/*
 * UStar PAX extended parsing
 */

named!(parse_ustar00_extra_pax<&[u8], PaxHeader>, chain!(
    atime:       parse_octal12                       ~
    ctime:       parse_octal12                       ~
    offset:      parse_octal12                       ~
    longnames:   parse_str4                          ~
    take!(1)                                         ~
    sparses:     apply!(parse_sparses_with_limit, 4) ~
    isextended:  parse_bool                          ~
    realsize:    parse_octal12                       ~
    take!(17), /* padding to 512 */
    ||{
        PaxHeader {
            atime:         atime,
            ctime:         ctime,
            offset:        offset,
            longnames:     longnames,
            sparses:       sparses,
            isextended:    isextended,
            realsize:      realsize,
        }
    }
));

/*
 * UStar Posix parsing
 */

named!(parse_ustar00_extra_posix<&[u8], UStarExtraHeader>, map!(pair!(parse_str155, take!(12)), |(prefix, _)| UStarExtraHeader::PosixUStar(PosixUStarHeader { prefix: prefix })));

fn parse_ustar00_extra<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], UStarExtraHeader<'a>> {
    match *flag {
        TypeFlag::PaxInterexchangeFormat => {
            chain!(i,
                mut header: parse_ustar00_extra_pax ~
                apply!(parse_pax_extra_sparses, &mut header),
                ||{
                    UStarExtraHeader::Pax(header)
                }
            )
        },
        _ => parse_ustar00_extra_posix(i)
    }
}

fn parse_ustar00<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], ExtraHeader<'a>> {
    chain!(i,
        tag!("00")             ~
        uname:    parse_str32  ~
        gname:    parse_str32  ~
        devmajor: parse_octal8 ~
        devminor: parse_octal8 ~
        extra:    apply!(parse_ustar00_extra, flag),
        ||{
            ExtraHeader::UStar(UStarHeader {
                magic:    "ustar\0",
                version:  "00",
                uname:    uname,
                gname:    gname,
                devmajor: devmajor,
                devminor: devminor,
                extra:    extra
            })
        }
    )
}

fn parse_ustar<'a, 'b>(i: &'a [u8], flag: &'b TypeFlag) -> IResult<&'a [u8], ExtraHeader<'a>> {
    map!(i, pair!(tag!("ustar\0"), apply!(parse_ustar00, flag)), |(_, ustar)| ustar)
}

/*
 * Posix tar archive header parsing
 */

named!(parse_posix<&[u8], ExtraHeader>, map!(take!(255), |_| ExtraHeader::Padding)); /* padding to 512 */

named!(parse_header<&[u8], PosixHeader>, chain!(
    name:     parse_str100    ~
    mode:     parse_str8      ~
    uid:      parse_octal8    ~
    gid:      parse_octal8    ~
    size:     parse_octal12   ~
    mtime:    parse_octal12   ~
    chksum:   parse_str8      ~
    typeflag: parse_type_flag ~
    linkname: parse_str100    ~
    ustar:    alt!(apply!(parse_ustar, &typeflag) | parse_posix),
    ||{
        PosixHeader {
            name:     name,
            mode:     mode,
            uid:      uid,
            gid:      gid,
            size:     size,
            mtime:    mtime,
            chksum:   chksum,
            typeflag: typeflag,
            linkname: linkname,
            ustar:    ustar
        }
    }
));

/*
 * Contents parsing
 */

fn parse_contents(i: &[u8], size: u64) -> IResult<&[u8], &[u8]> {
    let trailing = size % 512;
    let padding = match trailing {
        0 => 0,
        t => 512 - t
    };
    map!(i, pair!(take!(size as usize), take!(padding as usize)), |(contents, _)| contents)
}

/*
 * Tar entry header + contents parsing
 */

named!(parse_entry<&[u8], TarEntry>, chain!(
    header:   parse_header ~
    contents: apply!(parse_contents, header.size),
    ||{
        TarEntry {
            header: header,
            contents: contents
        }
    }
));

/*
 * Tar archive parsing
 */

fn filter_entries(entries: Vec<TarEntry>) -> Vec<TarEntry> {
    /* Filter out empty entries */
    entries.into_iter().filter(|e| e.header.name != "").collect::<Vec<TarEntry>>()
}

pub fn parse_tar(i: &[u8]) -> IResult<&[u8], Vec<TarEntry>> {
    map!(i, pair!(map!(many0!(parse_entry), filter_entries), eof), |(entries, _)| entries)
}

/*
 * Tests
 */

#[cfg(test)]
mod tests {
    use super::*;
    use std::str::from_utf8;
    use nom::IResult;

    #[test]
    fn octal_to_u64_ok_test() {
        assert_eq!(octal_to_u64("756"), Ok(494));
        assert_eq!(octal_to_u64(""), Ok(0));
    }

    #[test]
    fn octal_to_u64_error_test() {
        assert_eq!(octal_to_u64("1238"), Err("invalid octal string received"));
        assert_eq!(octal_to_u64("a"), Err("invalid octal string received"));
        assert_eq!(octal_to_u64("A"), Err("invalid octal string received"));
    }

    #[test]
    fn take_str_eat_garbage_test() {
        let s = b"foobar\0\0\0\0baz";
        let baz = b"baz";
        assert_eq!(take_str_eat_garbage!(&s[..], 10), IResult::Done(&baz[..], "foobar"));
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use Doc;

#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum Mode {
    Break,
    Flat,
}

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W>(pub W);

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.0.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W>(pub W);

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.0.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    writer: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(writer: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            writer,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.writer.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.writer.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.writer.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.writer.set_color(previous),
            None => self.writer.reset(),
        }
    }
}

type Cmd<'a, B, A> = (usize, Mode, &'a Doc<'a, B, A>);

fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
where
    W: ?Sized + Render,
{
    try!(out.write_str_all("\n"));
    write_spaces(ind, out)
}

fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
where
    W: ?Sized + Render,
{
    macro_rules! make_spaces {
        () => {
            ""
        };
        ($s: tt $($t: tt)*) => {
            concat!("          ", make_spaces!($($t)*))
        };
    }
    const SPACES: &str = make_spaces!(,,,,,,,,,,);
    let mut inserted = 0;
    while inserted < spaces {
        let insert = cmp::min(SPACES.len(), spaces - inserted);
        inserted += try!(out.write_str(&SPACES[..insert]));
    }
    Ok(())
}

#[inline]
fn fitting<'a, B, A>(
    next: Cmd<'a, B, A>,
    bcmds: &[Cmd<'a, B, A>],
    fcmds: &mut Vec<Cmd<'a, B, A>>,
    mut rem: isize,
) -> bool
where
    B: Deref<Target = Doc<'a, B, A>>,
{
    let mut bidx = bcmds.len();
    fcmds.clear(); // clear from previous calls from best
    fcmds.push(next);
    while rem >= 0 {
        match fcmds.pop() {
            None => {
                if bidx == 0 {
                    // All commands have been processed
                    return true;
                } else {
                    fcmds.push(bcmds[bidx - 1]);
                    bidx -= 1;
                }
            }
            Some((ind, mode, doc)) => {
                match *doc {
                    Doc::Nil => {}
                    Doc::Append(ref ldoc, ref rdoc) => {
                        fcmds.push((ind, mode, rdoc));
                        // Since appended documents often appear in sequence on the left side we
                        // gain a slight performance increase by batching these pushes (avoiding
                        // to push and directly pop `Append` documents)
                        let mut doc = ldoc;
                        while let Doc::Append(ref l, ref r) = **doc {
                            fcmds.push((ind, mode, r));
                            doc = l;
                        }
                        fcmds.push((ind, mode, doc));
                    }
                    Doc::Group(ref doc) => {
                        fcmds.push((ind, mode, doc));
                    }
                    Doc::Nest(off, ref doc) => {
                        fcmds.push((ind + off, mode, doc));
                    }
                    Doc::Space => match mode {
                        Mode::Flat => {
                            rem -= 1;
                        }
                        Mode::Break => {
                            return true;
                        }
                    },
                    Doc::Newline => return true,
                    Doc::Text(ref str) => {
                        rem -= str.len() as isize;
                    }
                    Doc::Annotated(_, ref doc) => fcmds.push((ind, mode, doc)),
                }
            }
        }
    }
    false
}

#[inline]
pub fn best<'a, W, B, A>(doc: &'a Doc<'a, B, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    B: Deref<Target = Doc<'a, B, A>>,
    W: ?Sized + RenderAnnotated<A>,
{
    let mut pos = 0usize;
    let mut bcmds = vec![(0usize, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some((ind, mode, doc)) = bcmds.pop() {
        match *doc {
            Doc::Nil => {}
            Doc::Append(ref ldoc, ref rdoc) => {
                bcmds.push((ind, mode, rdoc));
                let mut doc = ldoc;
                while let Doc::Append(ref l, ref r) = **doc {
                    bcmds.push((ind, mode, r));
                    doc = l;
                }
                bcmds.push((ind, mode, doc));
            }
            Doc::Group(ref doc) => match mode {
                Mode::Flat => {
                    bcmds.push((ind, Mode::Flat, doc));
                }
                Mode::Break => {
                    let next = (ind, Mode::Flat, &**doc);
                    let rem = width as isize - pos as isize;
                    if fitting(next, &bcmds, &mut fcmds, rem) {
                        bcmds.push(next);
                    } else {
                        bcmds.push((ind, Mode::Break, doc));
                    }
                }
            },
            Doc::Nest(off, ref doc) => {
                bcmds.push((ind + off, mode, doc));
            }
            Doc::Space => match mode {
                Mode::Flat => {
                    try!(write_spaces(1, out));
                }
                Mode::Break => {
                    try!(write_newline(ind, out));
                    pos = ind;
                }
            },
            Doc::Newline => {
                try!(write_newline(ind, out));
                pos = ind;

                // Since this newline caused an early break we don't know if the remaining
                // documents fit the next line so recalculate if they fit
                fcmds.clear();
                let docs = bcmds.len()
                    - bcmds
                        .iter()
                        .rev()
                        .position(|t| t.1 == Mode::Break)
                        .unwrap_or_else(|| bcmds.len());
                fcmds.extend_from_slice(&bcmds[docs..]);
                if let Some(next) = fcmds.pop() {
                    let rem = width as isize - pos as isize;
                    if !fitting(next, &bcmds, &mut fcmds, rem) {
                        for &mut (_, ref mut mode, _) in &mut bcmds[docs..] {
                            *mode = Mode::Break;
                        }
                    }
                }
            }
            Doc::Text(ref s) => {
                try!(out.write_str_all(s));
                pos += s.len();
            }
            Doc::Annotated(ref ann, ref doc) => {
                try!(out.push_annotation(ann));
                annotation_levels.push(bcmds.len());
                bcmds.push((ind, mode, doc));
            }
        }

        if annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            try!(out.pop_annotation());
        }
    }
    Ok(())
}
<commit_msg>Reduce the scoping of some functions and types<commit_after>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use Doc;

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W>(pub W);

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.0.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W>(pub W);

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.0.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    writer: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(writer: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            writer,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.writer.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.writer.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.writer.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.writer.set_color(previous),
            None => self.writer.reset(),
        }
    }
}

#[inline]
pub fn best<'a, W, B, A>(doc: &'a Doc<'a, B, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    B: Deref<Target = Doc<'a, B, A>>,
    W: ?Sized + RenderAnnotated<A>,
{
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    enum Mode {
        Break,
        Flat,
    }

    type Cmd<'a, B, A> = (usize, Mode, &'a Doc<'a, B, A>);

    fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        try!(out.write_str_all("\n"));
        write_spaces(ind, out)
    }

    fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        macro_rules! make_spaces {
            () => { "" };
            ($s: tt $($t: tt)*) => { concat!("          ", make_spaces!($($t)*)) };
        }

        const SPACES: &str = make_spaces!(,,,,,,,,,,);
        let mut inserted = 0;
        while inserted < spaces {
            let insert = cmp::min(SPACES.len(), spaces - inserted);
            inserted += try!(out.write_str(&SPACES[..insert]));
        }

        Ok(())
    }

    #[inline]
    fn fitting<'a, B, A>(
        next: Cmd<'a, B, A>,
        bcmds: &[Cmd<'a, B, A>],
        fcmds: &mut Vec<Cmd<'a, B, A>>,
        mut rem: isize,
    ) -> bool
    where
        B: Deref<Target = Doc<'a, B, A>>,
    {
        let mut bidx = bcmds.len();
        fcmds.clear(); // clear from previous calls from best
        fcmds.push(next);

        while rem >= 0 {
            match fcmds.pop() {
                None => {
                    if bidx == 0 {
                        // All commands have been processed
                        return true;
                    } else {
                        fcmds.push(bcmds[bidx - 1]);
                        bidx -= 1;
                    }
                }
                Some((ind, mode, doc)) => {
                    match *doc {
                        Doc::Nil => {}
                        Doc::Append(ref ldoc, ref rdoc) => {
                            fcmds.push((ind, mode, rdoc));
                            // Since appended documents often appear in sequence on the left side we
                            // gain a slight performance increase by batching these pushes (avoiding
                            // to push and directly pop `Append` documents)
                            let mut doc = ldoc;
                            while let Doc::Append(ref l, ref r) = **doc {
                                fcmds.push((ind, mode, r));
                                doc = l;
                            }
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Group(ref doc) => {
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Nest(off, ref doc) => {
                            fcmds.push((ind + off, mode, doc));
                        }
                        Doc::Space => match mode {
                            Mode::Flat => {
                                rem -= 1;
                            }
                            Mode::Break => {
                                return true;
                            }
                        },
                        Doc::Newline => return true,
                        Doc::Text(ref str) => {
                            rem -= str.len() as isize;
                        }
                        Doc::Annotated(_, ref doc) => fcmds.push((ind, mode, doc)),
                    }
                }
            }
        }

        false
    }

    let mut pos = 0usize;
    let mut bcmds = vec![(0usize, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some((ind, mode, doc)) = bcmds.pop() {
        match *doc {
            Doc::Nil => {}
            Doc::Append(ref ldoc, ref rdoc) => {
                bcmds.push((ind, mode, rdoc));
                let mut doc = ldoc;
                while let Doc::Append(ref l, ref r) = **doc {
                    bcmds.push((ind, mode, r));
                    doc = l;
                }
                bcmds.push((ind, mode, doc));
            }
            Doc::Group(ref doc) => match mode {
                Mode::Flat => {
                    bcmds.push((ind, Mode::Flat, doc));
                }
                Mode::Break => {
                    let next = (ind, Mode::Flat, &**doc);
                    let rem = width as isize - pos as isize;
                    if fitting(next, &bcmds, &mut fcmds, rem) {
                        bcmds.push(next);
                    } else {
                        bcmds.push((ind, Mode::Break, doc));
                    }
                }
            },
            Doc::Nest(off, ref doc) => {
                bcmds.push((ind + off, mode, doc));
            }
            Doc::Space => match mode {
                Mode::Flat => {
                    try!(write_spaces(1, out));
                }
                Mode::Break => {
                    try!(write_newline(ind, out));
                    pos = ind;
                }
            },
            Doc::Newline => {
                try!(write_newline(ind, out));
                pos = ind;

                // Since this newline caused an early break we don't know if the remaining
                // documents fit the next line so recalculate if they fit
                fcmds.clear();
                let docs = bcmds.len()
                    - bcmds
                        .iter()
                        .rev()
                        .position(|t| t.1 == Mode::Break)
                        .unwrap_or_else(|| bcmds.len());
                fcmds.extend_from_slice(&bcmds[docs..]);
                if let Some(next) = fcmds.pop() {
                    let rem = width as isize - pos as isize;
                    if !fitting(next, &bcmds, &mut fcmds, rem) {
                        for &mut (_, ref mut mode, _) in &mut bcmds[docs..] {
                            *mode = Mode::Break;
                        }
                    }
                }
            }
            Doc::Text(ref s) => {
                try!(out.write_str_all(s));
                pos += s.len();
            }
            Doc::Annotated(ref ann, ref doc) => {
                try!(out.push_annotation(ann));
                annotation_levels.push(bcmds.len());
                bcmds.push((ind, mode, doc));
            }
        }

        if annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            try!(out.pop_annotation());
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::io;
use std::io::Cursor;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::rc::Rc;

use byteorder::{LittleEndian, ReadBytesExt};
use digest::digest_reader;
use filebuffer::FileBuffer;
use sha2::Sha256;

use routing::EffectId;

/// Resource manager. Where to search for various file types (e.g. Effects).
/// Uses a 'dumb' implementation - doesn't try to auto-configure paths (/usr/bin/share/[...],
/// ~/.friendship, etc). Instead, designed to be configured by the host.
#[derive(Default, Debug)]
pub struct ResMan {
    dirs: Vec<PathBuf>,
}

/// Audio that may be on-disk.
#[derive(Clone, Debug)]
pub struct AudioBuffer {
    buffer: Rc<FileBuffer>,
}

impl ResMan {
    pub fn new() -> Self {
        Default::default()
    }
    pub fn add_dir(&mut self, dir: PathBuf) {
        self.dirs.push(dir);
    }
    /// Returns all definitions of the given effect in the form of an iterator
    ///   over boxed objects implementing io::Read.
    pub fn find_effect<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=(PathBuf, File)> + 'a {
        self.iter_effect_files(id).map(|path| {
            (path.clone(), File::open(path).unwrap())
        })
    }
    fn iter_effect_files<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=PathBuf> + 'a {
        self.iter_all_files().filter(move |f| {
            trace!("Resman: testing hash for: {:?}", f);
            match *id.sha256() {
                None => true,
                Some(ref hash) => {
                    let mut file = File::open(f).unwrap();
                    let result = digest_reader::<Sha256>(&mut file).unwrap();
                    hash == result.as_slice()
                }
            }
        })
    }
    fn iter_all_files<'a>(&'a self) -> impl Iterator<Item=PathBuf> + 'a {
        // dirs as PathBuf -> valid ReadDir objects
        self.dirs.iter().filter_map(|dir_path| {
            fs::read_dir(dir_path)
                .map_err(|e| warn!("ResMan: Failed to read directory {:?}: {}", dir_path, e))
                .ok()
        })
        // ReadDir objects -> flat list of Result<DirEntry>
        .flat_map(|read_dir| {
            read_dir
        })
        // Result<DirEntry> -> DirEntry
        .filter_map(|dir_entry| {
            dir_entry
                .map_err(|e| warn!("ResMan: Failed to read directory entry: {}", e))
                .ok()
        })
        // keep only the files
        .filter(|dir_entry| {
            if let Ok(file_type)=dir_entry.file_type() {
                file_type.is_file()
            } else {
                false
            }
        })
        // DirEntry -> Path
        .map(|dir_entry| {
            dir_entry.path()
        })
    }
}

impl AudioBuffer {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
        if path.as_ref().extension().map(|e| e == "f32").unwrap_or(false) {
            // TODO: don't abort on failure; instead, treat as zero stream
            Ok(Self {
                buffer: Rc::new(FileBuffer::open(path)?)
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("Unknown audio format for file: {:?}", path.as_ref())))
        }
    }
    /// Read data from the buffer.
    pub fn get(&self, idx: u64, ch: u32) -> f32 {
        assert_eq!(ch, 0);
        // TODO: this isn't very dependable for 32-bit OSes.
        let idx = idx*4; // frame index -> byte index
        let view = &self.buffer[idx as usize..idx as usize + 4];
        let mut reader = Cursor::new(view);
        // Read float or 0f32 if error (e.g. end of file?)
        reader.read_f32::<LittleEndian>().unwrap_or(0f32)
    }
}
<commit_msg>More verbose logging for Resman<commit_after>use std::path::PathBuf;
use std::io;
use std::io::Cursor;
use std::fs;
use std::fs::File;
use std::path::Path;
use std::rc::Rc;

use byteorder::{LittleEndian, ReadBytesExt};
use digest::digest_reader;
use filebuffer::FileBuffer;
use sha2::Sha256;

use routing::EffectId;

/// Resource manager. Where to search for various file types (e.g. Effects).
/// Uses a 'dumb' implementation - doesn't try to auto-configure paths (/usr/bin/share/[...],
/// ~/.friendship, etc). Instead, designed to be configured by the host.
#[derive(Default, Debug)]
pub struct ResMan {
    dirs: Vec<PathBuf>,
}

/// Audio that may be on-disk.
#[derive(Clone, Debug)]
pub struct AudioBuffer {
    buffer: Rc<FileBuffer>,
}

impl ResMan {
    pub fn new() -> Self {
        Default::default()
    }
    pub fn add_dir(&mut self, dir: PathBuf) {
        self.dirs.push(dir);
    }
    /// Returns all definitions of the given effect in the form of an iterator
    ///   over boxed objects implementing io::Read.
    pub fn find_effect<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=(PathBuf, File)> + 'a {
        self.iter_effect_files(id).map(|path| {
            (path.clone(), File::open(path).unwrap())
        })
    }
    fn iter_effect_files<'a>(&'a self, id: &'a EffectId) -> impl Iterator<Item=PathBuf> + 'a {
        self.iter_all_files().filter(move |f| {
            let did_match = match *id.sha256() {
                None => true,
                Some(ref hash) => {
                    let mut file = File::open(f).unwrap();
                    let result = digest_reader::<Sha256>(&mut file).unwrap();
                    hash == result.as_slice()
                }
            };
            trace!("Resman: testing hash for: {:?} ({:?})", f, did_match);
            did_match
        })
    }
    fn iter_all_files<'a>(&'a self) -> impl Iterator<Item=PathBuf> + 'a {
        // dirs as PathBuf -> valid ReadDir objects
        self.dirs.iter().filter_map(|dir_path| {
            fs::read_dir(dir_path)
                .map_err(|e| warn!("ResMan: Failed to read directory {:?}: {}", dir_path, e))
                .ok()
        })
        // ReadDir objects -> flat list of Result<DirEntry>
        .flat_map(|read_dir| {
            read_dir
        })
        // Result<DirEntry> -> DirEntry
        .filter_map(|dir_entry| {
            dir_entry
                .map_err(|e| warn!("ResMan: Failed to read directory entry: {}", e))
                .ok()
        })
        // keep only the files
        .filter(|dir_entry| {
            if let Ok(file_type)=dir_entry.file_type() {
                file_type.is_file()
            } else {
                false
            }
        })
        // DirEntry -> Path
        .map(|dir_entry| {
            dir_entry.path()
        })
    }
}

impl AudioBuffer {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
        if path.as_ref().extension().map(|e| e == "f32").unwrap_or(false) {
            // TODO: don't abort on failure; instead, treat as zero stream
            Ok(Self {
                buffer: Rc::new(FileBuffer::open(path)?)
            })
        } else {
            Err(io::Error::new(io::ErrorKind::Other, format!("Unknown audio format for file: {:?}", path.as_ref())))
        }
    }
    /// Read data from the buffer.
    pub fn get(&self, idx: u64, ch: u32) -> f32 {
        assert_eq!(ch, 0);
        // TODO: this isn't very dependable for 32-bit OSes.
        let idx = idx*4; // frame index -> byte index
        let view = &self.buffer[idx as usize..idx as usize + 4];
        let mut reader = Cursor::new(view);
        // Read float or 0f32 if error (e.g. end of file?)
        reader.read_f32::<LittleEndian>().unwrap_or(0f32)
    }
}
<|endoftext|>


<commit_before>use std::process;
use std::process::{Command, Stdio};
use std::io::{Result as IoResult, Error as IoError};
use std::path::{Path, PathBuf};
use std::error::Error;
use std::convert::From;
use std::fmt;

use mozprofile::profile::Profile;
use mozprofile::prefdata::FIREFOX_PREFERENCES;
use mozprofile::prefreader::PrefReaderError;

pub trait Runner {
    fn start(&mut self) -> Result<(), RunnerError>;

    fn build_command(&self, &mut Command);

    fn is_running(&self) -> bool;

    fn stop(&mut self) -> IoResult<Option<process::ExitStatus>>;
}

#[derive(Debug)]
pub enum RunnerError {
    Io(IoError),
    PrefReader(PrefReaderError)
}

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

impl Error for RunnerError {
    fn description(&self) -> &str {
        match *self {
            RunnerError::Io(ref err) => err.description().clone(),
            RunnerError::PrefReader(ref err) => err.description().clone(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            RunnerError::Io(ref err) => err.cause(),
            RunnerError::PrefReader(ref err) => err.cause(),
        }
    }
}

impl From<IoError> for RunnerError {
    fn from(value: IoError) -> RunnerError {
        RunnerError::Io(value)
    }
}

impl From<PrefReaderError> for RunnerError {
    fn from(value: PrefReaderError) -> RunnerError {
        RunnerError::PrefReader(value)
    }
}

pub struct FirefoxRunner {
    pub binary: PathBuf,
    args: Vec<String>,
    process: Option<process::Child>,
    pub ret_code: Option<process::ExitStatus>,
    pub profile: Profile
}

impl FirefoxRunner {
    pub fn new(binary: &Path, profile: Option<Profile>) -> IoResult<FirefoxRunner> {
        let prof = match profile {
            Some(p) => p,
            None => try!(Profile::new(None))
        };

        Ok(FirefoxRunner {
            binary: binary.to_path_buf(),
            process: None,
            ret_code: None,
            args: Vec::new(),
            profile: prof
        })
    }
}

impl Runner for FirefoxRunner {
    fn start(&mut self) -> Result<(), RunnerError> {
        let mut cmd = Command::new(&self.binary);
        self.build_command(&mut cmd);

        let mut prefs = try!(self.profile.user_prefs());
        prefs.insert_slice(&FIREFOX_PREFERENCES[..]);

        try!(prefs.write());

        let process = try!(cmd.spawn());
        self.process = Some(process);
        Ok(())
    }

    fn build_command(&self, command: &mut Command) {
        command.args(&self.args[..]).arg("--marionette").arg("--profile").arg(&self.profile.path)
            .stdout(Stdio::inherit()).stderr(Stdio::inherit())
            .env("MOZ_NO_REMOTE", "1").env("NO_EM_RESTART", "1");
    }

    fn is_running(&self) -> bool {
        self.process.is_some() && self.ret_code.is_none()
    }

    fn stop(&mut self) -> IoResult<Option<process::ExitStatus>> {
        match self.process.as_mut() {
            Some(p) => {
                try!(p.kill());
                let status = try!(p.wait());
                self.ret_code = Some(status);
            },
            None => {}
        };
        Ok(self.ret_code)
    }
}
<commit_msg>Expose command line arguments in Runner trait.<commit_after>use std::process;
use std::process::{Command, Stdio};
use std::io::{Result as IoResult, Error as IoError};
use std::path::{Path, PathBuf};
use std::error::Error;
use std::convert::From;
use std::fmt;

use mozprofile::profile::Profile;
use mozprofile::prefdata::FIREFOX_PREFERENCES;
use mozprofile::prefreader::PrefReaderError;

pub trait Runner {
    fn start(&mut self) -> Result<(), RunnerError>;

    fn args(&mut self) -> &mut Vec<String>;

    fn build_command(&self, &mut Command);

    fn is_running(&self) -> bool;

    fn stop(&mut self) -> IoResult<Option<process::ExitStatus>>;
}

#[derive(Debug)]
pub enum RunnerError {
    Io(IoError),
    PrefReader(PrefReaderError)
}

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

impl Error for RunnerError {
    fn description(&self) -> &str {
        match *self {
            RunnerError::Io(ref err) => err.description().clone(),
            RunnerError::PrefReader(ref err) => err.description().clone(),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            RunnerError::Io(ref err) => err.cause(),
            RunnerError::PrefReader(ref err) => err.cause(),
        }
    }
}

impl From<IoError> for RunnerError {
    fn from(value: IoError) -> RunnerError {
        RunnerError::Io(value)
    }
}

impl From<PrefReaderError> for RunnerError {
    fn from(value: PrefReaderError) -> RunnerError {
        RunnerError::PrefReader(value)
    }
}

pub struct FirefoxRunner {
    pub binary: PathBuf,
    args: Vec<String>,
    process: Option<process::Child>,
    pub ret_code: Option<process::ExitStatus>,
    pub profile: Profile
}

impl FirefoxRunner {
    pub fn new(binary: &Path, profile: Option<Profile>) -> IoResult<FirefoxRunner> {
        let prof = match profile {
            Some(p) => p,
            None => try!(Profile::new(None))
        };

        Ok(FirefoxRunner {
            binary: binary.to_path_buf(),
            process: None,
            ret_code: None,
            args: Vec::new(),
            profile: prof
        })
    }
}

impl Runner for FirefoxRunner {
    fn start(&mut self) -> Result<(), RunnerError> {
        let mut cmd = Command::new(&self.binary);
        self.build_command(&mut cmd);

        let mut prefs = try!(self.profile.user_prefs());
        prefs.insert_slice(&FIREFOX_PREFERENCES[..]);

        try!(prefs.write());

        let process = try!(cmd.spawn());
        self.process = Some(process);
        Ok(())
    }

    fn args(&mut self) -> &mut Vec<String> {
        &mut self.args
    }

    fn build_command(&self, command: &mut Command) {
        command.args(&self.args[..]).arg("--marionette").arg("--profile").arg(&self.profile.path)
            .stdout(Stdio::inherit()).stderr(Stdio::inherit())
            .env("MOZ_NO_REMOTE", "1").env("NO_EM_RESTART", "1");
    }

    fn is_running(&self) -> bool {
        self.process.is_some() && self.ret_code.is_none()
    }

    fn stop(&mut self) -> IoResult<Option<process::ExitStatus>> {
        match self.process.as_mut() {
            Some(p) => {
                try!(p.kill());
                let status = try!(p.wait());
                self.ret_code = Some(status);
            },
            None => {}
        };
        Ok(self.ret_code)
    }
}
<|endoftext|>


<commit_before>//! # runner
//!
//! Runs the requested tasks.

//! The flow is as follows:
//!
//! * Load env variables
//! * Create an execution plan based on the requested task and its dependencies
//! * Run all tasks defined in the execution plan
//!

#[cfg(test)]
#[path = "./runner_test.rs"]
mod runner_test;

use crate::command;
use crate::condition;
use crate::environment;
use crate::execution_plan::create as create_execution_plan;
use crate::installer;
use crate::logger;
use crate::profile;
use crate::scriptengine;
use crate::types::{CliArgs, Config, EnvInfo, EnvValue, ExecutionPlan, FlowInfo, Step, Task};
use indexmap::IndexMap;
use std::env;
use std::time::SystemTime;

fn validate_condition(flow_info: &FlowInfo, step: &Step) -> bool {
    condition::validate_condition(&flow_info, &step)
}

fn run_sub_task(flow_info: &FlowInfo, sub_task: &str) {
    let mut sub_flow_info = flow_info.clone();
    sub_flow_info.task = sub_task.to_string();

    run_flow(&sub_flow_info, true);
}

fn create_watch_task_name(task: &str) -> String {
    let mut watch_task_name = "".to_string();
    watch_task_name.push_str(&task);
    watch_task_name.push_str("-watch");

    watch_task_name
}

fn create_watch_step(task: &str) -> Step {
    let watch_task = create_watch_task(&task);

    let watch_task_name = create_watch_task_name(&task);

    Step {
        name: watch_task_name,
        config: watch_task,
    }
}

fn watch_task(flow_info: &FlowInfo, task: &str) {
    let step = create_watch_step(&task);

    run_task(&flow_info, &step);
}

fn should_watch(task: &Task) -> bool {
    match task.watch {
        Some(watch_bool) => {
            if watch_bool {
                let disable_watch_env = environment::get_env("CARGO_MAKE_DISABLE_WATCH", "FALSE");
                disable_watch_env != "TRUE"
            } else {
                false
            }
        }
        None => false,
    }
}

fn run_task(flow_info: &FlowInfo, step: &Step) {
    info!("Running Task: {}", &step.name);

    if validate_condition(&flow_info, &step) {
        if !step.config.is_valid() {
            error!(
                "Invalid task, contains multiple actions.\n{:#?}",
                &step.config
            );
        }

        let env = match step.config.env {
            Some(ref env) => env.clone(),
            None => IndexMap::new(),
        };
        environment::set_env(env);

        let updated_step = environment::expand_env(&step);

        let watch = should_watch(&step.config);

        if watch {
            watch_task(&flow_info, &step.name);
        } else {
            installer::install(&updated_step.config);

            match step.config.run_task {
                Some(ref sub_task) => run_sub_task(&flow_info, sub_task),
                None => {
                    let revert_directory = match step.config.cwd {
                        Some(ref cwd) => {
                            if cwd.len() > 0 {
                                let directory =
                                    environment::get_env("CARGO_MAKE_WORKING_DIRECTORY", "");

                                environment::setup_cwd(Some(cwd));

                                directory
                            } else {
                                "".to_string()
                            }
                        }
                        None => "".to_string(),
                    };

                    // get cli arguments
                    let cli_arguments = match flow_info.cli_arguments {
                        Some(ref args) => args.clone(),
                        None => vec![],
                    };

                    // run script
                    let script_runner_done =
                        scriptengine::invoke(&updated_step.config, &cli_arguments);

                    // run command
                    if !script_runner_done {
                        command::run(&updated_step);
                    };

                    // revert to original cwd
                    match step.config.cwd {
                        Some(_) => environment::setup_cwd(Some(&revert_directory)),
                        _ => (),
                    };
                }
            };
        }
    } else {
        debug!("Task: {} disabled", &step.name);
    }
}

fn run_task_flow(flow_info: &FlowInfo, execution_plan: &ExecutionPlan) {
    for step in &execution_plan.steps {
        run_task(&flow_info, &step);
    }
}

fn create_watch_task(task: &str) -> Task {
    let mut task_config = create_proxy_task(&task);

    let mut env_map = task_config.env.unwrap_or(IndexMap::new());
    env_map.insert(
        "CARGO_MAKE_DISABLE_WATCH".to_string(),
        EnvValue::Value("TRUE".to_string()),
    );
    task_config.env = Some(env_map);

    let make_args = task_config.args.unwrap();
    let make_command = &make_args.join(" ");

    let watch_args = vec![
        "watch".to_string(),
        "-q".to_string(),
        "-x".to_string(),
        make_command.to_string(),
    ];
    task_config.args = Some(watch_args);

    task_config
}

fn create_proxy_task(task: &str) -> Task {
    //get log level name
    let log_level = logger::get_log_level();

    let mut log_level_arg = "--loglevel=".to_string();
    log_level_arg.push_str(&log_level);

    //get profile
    let profile_name = profile::get();

    let mut profile_arg = "--profile=\"".to_string();
    profile_arg.push_str(&profile_name);
    profile_arg.push_str("\"");

    //setup common args
    let mut args = vec![
        "make".to_string(),
        "--disable-check-for-updates".to_string(),
        "--no-on-error".to_string(),
        log_level_arg.to_string(),
        profile_arg.to_string(),
    ];

    //get makefile location
    match env::var("CARGO_MAKE_MAKEFILE_PATH") {
        Ok(makefile_path) => {
            if makefile_path.len() > 0 {
                let mut makefile_arg = "--makefile ".to_string();
                makefile_arg.push_str(&makefile_path);

                args.push(makefile_arg.to_string());
            }
        }
        _ => {}
    };

    args.push(task.to_string());

    let mut proxy_task = Task::new();
    proxy_task.command = Some("cargo".to_string());
    proxy_task.args = Some(args);

    proxy_task.get_normalized_task()
}

fn run_flow(flow_info: &FlowInfo, allow_private: bool) {
    let execution_plan = create_execution_plan(
        &flow_info.config,
        &flow_info.task,
        flow_info.disable_workspace,
        allow_private,
    );
    debug!("Created execution plan: {:#?}", &execution_plan);

    run_task_flow(&flow_info, &execution_plan);
}

fn run_protected_flow(flow_info: &FlowInfo) {
    let proxy_task = create_proxy_task(&flow_info.task);

    let exit_code = command::run_command(&proxy_task.command.unwrap(), &proxy_task.args, false);

    if exit_code != 0 {
        match flow_info.config.config.on_error_task {
            Some(ref on_error_task) => {
                let mut error_flow_info = flow_info.clone();
                error_flow_info.disable_on_error = true;
                error_flow_info.task = on_error_task.clone();

                run_flow(&error_flow_info, false);
            }
            _ => (),
        };

        error!("Task error detected, exit code: {}", &exit_code);
    }
}

/// Runs the requested tasks.

/// The flow is as follows:
///
/// * Create an execution plan based on the requested task and its dependencies
/// * Run all tasks defined in the execution plan
pub(crate) fn run(config: Config, task: &str, env_info: EnvInfo, cli_args: &CliArgs) {
    let start_time = SystemTime::now();

    let flow_info = FlowInfo {
        config,
        task: task.to_string(),
        env_info,
        disable_workspace: cli_args.disable_workspace,
        disable_on_error: cli_args.disable_on_error,
        cli_arguments: cli_args.arguments.clone(),
    };

    if flow_info.disable_on_error || flow_info.config.config.on_error_task.is_none() {
        run_flow(&flow_info, false);
    } else {
        run_protected_flow(&flow_info);
    }

    let time_string = match start_time.elapsed() {
        Ok(elapsed) => {
            let mut string = " in ".to_string();
            string.push_str(&elapsed.as_secs().to_string());
            string.push_str(" seconds");

            string
        }
        _ => "".to_string(),
    };

    info!("Build Done {}.", &time_string);
}
<commit_msg>Profile support #174<commit_after>//! # runner
//!
//! Runs the requested tasks.

//! The flow is as follows:
//!
//! * Load env variables
//! * Create an execution plan based on the requested task and its dependencies
//! * Run all tasks defined in the execution plan
//!

#[cfg(test)]
#[path = "./runner_test.rs"]
mod runner_test;

use crate::command;
use crate::condition;
use crate::environment;
use crate::execution_plan::create as create_execution_plan;
use crate::installer;
use crate::logger;
use crate::profile;
use crate::scriptengine;
use crate::types::{CliArgs, Config, EnvInfo, EnvValue, ExecutionPlan, FlowInfo, Step, Task};
use indexmap::IndexMap;
use std::env;
use std::time::SystemTime;

fn validate_condition(flow_info: &FlowInfo, step: &Step) -> bool {
    condition::validate_condition(&flow_info, &step)
}

fn run_sub_task(flow_info: &FlowInfo, sub_task: &str) {
    let mut sub_flow_info = flow_info.clone();
    sub_flow_info.task = sub_task.to_string();

    run_flow(&sub_flow_info, true);
}

fn create_watch_task_name(task: &str) -> String {
    let mut watch_task_name = "".to_string();
    watch_task_name.push_str(&task);
    watch_task_name.push_str("-watch");

    watch_task_name
}

fn create_watch_step(task: &str) -> Step {
    let watch_task = create_watch_task(&task);

    let watch_task_name = create_watch_task_name(&task);

    Step {
        name: watch_task_name,
        config: watch_task,
    }
}

fn watch_task(flow_info: &FlowInfo, task: &str) {
    let step = create_watch_step(&task);

    run_task(&flow_info, &step);
}

fn should_watch(task: &Task) -> bool {
    match task.watch {
        Some(watch_bool) => {
            if watch_bool {
                let disable_watch_env = environment::get_env("CARGO_MAKE_DISABLE_WATCH", "FALSE");
                disable_watch_env != "TRUE"
            } else {
                false
            }
        }
        None => false,
    }
}

fn run_task(flow_info: &FlowInfo, step: &Step) {
    info!("Running Task: {}", &step.name);

    if validate_condition(&flow_info, &step) {
        if !step.config.is_valid() {
            error!(
                "Invalid task, contains multiple actions.\n{:#?}",
                &step.config
            );
        }

        //get profile
        let profile_name = profile::get();

        let env = match step.config.env {
            Some(ref env) => env.clone(),
            None => IndexMap::new(),
        };
        environment::set_env(env);

        //make sure profile env is not overwritten
        profile::set(&profile_name);

        let updated_step = environment::expand_env(&step);

        let watch = should_watch(&step.config);

        if watch {
            watch_task(&flow_info, &step.name);
        } else {
            installer::install(&updated_step.config);

            match step.config.run_task {
                Some(ref sub_task) => run_sub_task(&flow_info, sub_task),
                None => {
                    let revert_directory = match step.config.cwd {
                        Some(ref cwd) => {
                            if cwd.len() > 0 {
                                let directory =
                                    environment::get_env("CARGO_MAKE_WORKING_DIRECTORY", "");

                                environment::setup_cwd(Some(cwd));

                                directory
                            } else {
                                "".to_string()
                            }
                        }
                        None => "".to_string(),
                    };

                    // get cli arguments
                    let cli_arguments = match flow_info.cli_arguments {
                        Some(ref args) => args.clone(),
                        None => vec![],
                    };

                    // run script
                    let script_runner_done =
                        scriptengine::invoke(&updated_step.config, &cli_arguments);

                    // run command
                    if !script_runner_done {
                        command::run(&updated_step);
                    };

                    // revert to original cwd
                    match step.config.cwd {
                        Some(_) => environment::setup_cwd(Some(&revert_directory)),
                        _ => (),
                    };
                }
            };
        }
    } else {
        debug!("Task: {} disabled", &step.name);
    }
}

fn run_task_flow(flow_info: &FlowInfo, execution_plan: &ExecutionPlan) {
    for step in &execution_plan.steps {
        run_task(&flow_info, &step);
    }
}

fn create_watch_task(task: &str) -> Task {
    let mut task_config = create_proxy_task(&task);

    let mut env_map = task_config.env.unwrap_or(IndexMap::new());
    env_map.insert(
        "CARGO_MAKE_DISABLE_WATCH".to_string(),
        EnvValue::Value("TRUE".to_string()),
    );
    task_config.env = Some(env_map);

    let make_args = task_config.args.unwrap();
    let make_command = &make_args.join(" ");

    let watch_args = vec![
        "watch".to_string(),
        "-q".to_string(),
        "-x".to_string(),
        make_command.to_string(),
    ];
    task_config.args = Some(watch_args);

    task_config
}

fn create_proxy_task(task: &str) -> Task {
    //get log level name
    let log_level = logger::get_log_level();

    let mut log_level_arg = "--loglevel=".to_string();
    log_level_arg.push_str(&log_level);

    //get profile
    let profile_name = profile::get();

    let mut profile_arg = "--profile=\"".to_string();
    profile_arg.push_str(&profile_name);
    profile_arg.push_str("\"");

    //setup common args
    let mut args = vec![
        "make".to_string(),
        "--disable-check-for-updates".to_string(),
        "--no-on-error".to_string(),
        log_level_arg.to_string(),
        profile_arg.to_string(),
    ];

    //get makefile location
    match env::var("CARGO_MAKE_MAKEFILE_PATH") {
        Ok(makefile_path) => {
            if makefile_path.len() > 0 {
                let mut makefile_arg = "--makefile ".to_string();
                makefile_arg.push_str(&makefile_path);

                args.push(makefile_arg.to_string());
            }
        }
        _ => {}
    };

    args.push(task.to_string());

    let mut proxy_task = Task::new();
    proxy_task.command = Some("cargo".to_string());
    proxy_task.args = Some(args);

    proxy_task.get_normalized_task()
}

fn run_flow(flow_info: &FlowInfo, allow_private: bool) {
    let execution_plan = create_execution_plan(
        &flow_info.config,
        &flow_info.task,
        flow_info.disable_workspace,
        allow_private,
    );
    debug!("Created execution plan: {:#?}", &execution_plan);

    run_task_flow(&flow_info, &execution_plan);
}

fn run_protected_flow(flow_info: &FlowInfo) {
    let proxy_task = create_proxy_task(&flow_info.task);

    let exit_code = command::run_command(&proxy_task.command.unwrap(), &proxy_task.args, false);

    if exit_code != 0 {
        match flow_info.config.config.on_error_task {
            Some(ref on_error_task) => {
                let mut error_flow_info = flow_info.clone();
                error_flow_info.disable_on_error = true;
                error_flow_info.task = on_error_task.clone();

                run_flow(&error_flow_info, false);
            }
            _ => (),
        };

        error!("Task error detected, exit code: {}", &exit_code);
    }
}

/// Runs the requested tasks.

/// The flow is as follows:
///
/// * Create an execution plan based on the requested task and its dependencies
/// * Run all tasks defined in the execution plan
pub(crate) fn run(config: Config, task: &str, env_info: EnvInfo, cli_args: &CliArgs) {
    let start_time = SystemTime::now();

    let flow_info = FlowInfo {
        config,
        task: task.to_string(),
        env_info,
        disable_workspace: cli_args.disable_workspace,
        disable_on_error: cli_args.disable_on_error,
        cli_arguments: cli_args.arguments.clone(),
    };

    if flow_info.disable_on_error || flow_info.config.config.on_error_task.is_none() {
        run_flow(&flow_info, false);
    } else {
        run_protected_flow(&flow_info);
    }

    let time_string = match start_time.elapsed() {
        Ok(elapsed) => {
            let mut string = " in ".to_string();
            string.push_str(&elapsed.as_secs().to_string());
            string.push_str(" seconds");

            string
        }
        _ => "".to_string(),
    };

    info!("Build Done {}.", &time_string);
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![allow(dead_code)]
// This is experimental for now...

use ::capnp::Word;
use ::capnp::private::RawSchema;
use ::std::cell::RefCell;

pub struct Arena {
    allocations : RefCell<Vec<Vec<Word>>>,
}

impl Arena {
    pub fn new() -> Arena {
        Arena { allocations : RefCell::new(Vec::new()) }
    }

    pub fn alloc<'a>(&'a self, length : usize) -> &'a mut [Word] {
        let mut v = ::capnp::Word::allocate_zeroed_vec(length);
        let result = unsafe {::std::slice::from_raw_parts_mut(v.as_mut_ptr(), v.len())};
        self.allocations.borrow_mut().push(v);
        result
    }
}

pub struct SchemaLoader<'a> {
    arena : &'a Arena,
    schemas : ::std::collections::hash_map::HashMap<u64, RawSchema<'a>>,
}

impl <'a> SchemaLoader<'a> {
    pub fn new(arena : &'a Arena) -> SchemaLoader<'a> {
        SchemaLoader {
            arena : arena,
            schemas : ::std::collections::hash_map::HashMap::new(),
        }
    }

    pub fn load(&mut self, reader : ::schema_capnp::node::Reader) {
        let id = reader.get_id();
        let num_words = reader.total_size().unwrap().word_count;
        self.schemas.get(&id);
        let _words = self.arena.alloc(num_words as usize);
        //ScratchSpaceMallocMessageBuilder::new
    }
}
<commit_msg>delete use of capnp::private::RawSchema<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#![allow(dead_code)]
// This is experimental for now...

use ::capnp::Word;
use ::std::cell::RefCell;

pub struct Arena {
    allocations : RefCell<Vec<Vec<Word>>>,
}

impl Arena {
    pub fn new() -> Arena {
        Arena { allocations : RefCell::new(Vec::new()) }
    }

    pub fn alloc<'a>(&'a self, length : usize) -> &'a mut [Word] {
        let mut v = ::capnp::Word::allocate_zeroed_vec(length);
        let result = unsafe {::std::slice::from_raw_parts_mut(v.as_mut_ptr(), v.len())};
        self.allocations.borrow_mut().push(v);
        result
    }
}

pub struct SchemaLoader<'a> {
    arena : &'a Arena,
    schemas : ::std::collections::hash_map::HashMap<u64, &'a [Word]>,
}

impl <'a> SchemaLoader<'a> {
    pub fn new(arena : &'a Arena) -> SchemaLoader<'a> {
        SchemaLoader {
            arena : arena,
            schemas : ::std::collections::hash_map::HashMap::new(),
        }
    }

    pub fn load(&mut self, reader : ::schema_capnp::node::Reader) {
        let id = reader.get_id();
        let num_words = reader.total_size().unwrap().word_count;
        self.schemas.get(&id);
        let _words = self.arena.alloc(num_words as usize);
        //ScratchSpaceMallocMessageBuilder::new
    }
}
<|endoftext|>