<commit_before>/*
 * Copyright (c) 2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::cmp;
use std::iter::AdditiveIterator;

use buffer;
use screen;

const MIN_VIEW_SIZE: u16 = 1;

#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(test, derive(Debug))]
pub enum CaretMovement {
  LineUp,
  LineDown,
  CharNext,
  CharPrev,
}

/*
 * View handles the caret and presentation of a buffer.
 * Scroll and size are both measured in screen cell coordinates. While the caret
 * is in buffer coordinates, the saved column is in screen coordinates.
 */
pub struct View {
  scroll_line: usize,
  scroll_column: usize,
  size: screen::Size,
  caret_line: usize,
  caret_column: usize,
  saved_column: Option<usize>,
}

impl View {
  pub fn new() -> View {
    View {
      scroll_line: 0,
      scroll_column: 0,
      size: screen::Size(MIN_VIEW_SIZE, MIN_VIEW_SIZE),
      caret_line: 0,
      caret_column: 0,
      saved_column: None,
    }
  }

  fn scroll_into_view(&mut self, line: usize, column: usize,
      buffer: &buffer::Buffer) {
    let screen::Size(rows, cols) = self.size;
    let rows = rows as usize;
    let cols = cols as usize;

    self.scroll_line =
      if line < self.scroll_line { line }
      else if line >= self.scroll_line + rows { line - rows + 1 }
      else { self.scroll_line };

    // make sure wider characters are scrolled in entirely as well
    let character = buffer.get_char_by_line_column(line, column).unwrap();
    let start = buffer_to_screen_column(line, column, buffer);
    let end = start + character.width(false).unwrap_or(1) - 1;
    self.scroll_column =
      if start < self.scroll_column { start }
      else if end >= self.scroll_column + cols { end - cols + 1 }
      else { self.scroll_column };
  }

  // Moves the caret and preserves its old screen space column when moving to a
  // line that can't have the caret at the same column.
  pub fn move_caret(&mut self, movement: CaretMovement,
      buffer: &buffer::Buffer) {
    let (line, column) = (self.caret_line, self.caret_column);
    let (new_line, new_column, new_saved_column) = match movement {
      CaretMovement::CharPrev =>
        (line, cmp::max(0, column as isize - 1) as usize, None),
      CaretMovement::CharNext => {
        let line_length = buffer.line_length(line).unwrap();
        let max_column = cmp::max(0, line_length as isize - 1) as usize;
        (line, cmp::min(max_column, column + 1), None)
      }
      CaretMovement::LineUp   =>
        if line == 0 { (line, column, self.saved_column) }
        else { self.vertical_caret_movement(line, line - 1, buffer) },
      CaretMovement::LineDown => {
        let max_line = cmp::max(0, buffer.num_lines() as isize - 1) as usize;
        if line == max_line { (line, column, self.saved_column) }
        else { self.vertical_caret_movement(line, line + 1, buffer) }
      }
    };
    if line != new_line || column != new_column {
      self.caret_line = new_line;
      self.caret_column = new_column;
      self.saved_column = new_saved_column;
      self.scroll_into_view(new_line, new_column, buffer);
    }
  }

  // helper function to move_caret, restricts the caret column to valid
  // character positions in screen space
  fn vertical_caret_movement(&self, from_line: usize, to_line: usize,
      buffer: &buffer::Buffer) -> (usize, usize, Option<usize>) {
    let to_line_length = buffer.line_length(to_line).unwrap();
    let to_line_screen_length =
      buffer_to_screen_column(to_line, to_line_length, buffer);
    let max_column = cmp::max(0, to_line_screen_length as isize - 1) as usize;
    let desired_column = self.saved_column.unwrap_or(
      buffer_to_screen_column(from_line, self.caret_column, buffer));
    let screen_column = cmp::min(max_column, desired_column);
    let buffer_column =
      screen_to_buffer_column(to_line, screen_column, buffer).unwrap();
    (to_line, buffer_column,
      if buffer_column == desired_column { None } else { Some(desired_column) })
  }

  pub fn set_size(&mut self, size: screen::Size, buffer: &buffer::Buffer) {
    let screen::Size(rows, cols) = size;
    assert!(rows >= MIN_VIEW_SIZE && cols >= MIN_VIEW_SIZE);
    self.size = size;
    let (line, column) = (self.caret_line, self.caret_column);
    self.scroll_into_view(line, column, buffer);
  }

  #[cfg(not(test))]
  pub fn draw(&self, buffer: &buffer::Buffer, focused: bool,
      position: screen::Cell, screen: &mut screen::Screen) {
    // calculate caret screen position
    let caret_row = (self.caret_line - self.scroll_line) as u16;
    let caret_column = buffer_to_screen_column(
      self.caret_line, self.caret_column, buffer) - self.scroll_column;
    let caret_cell = position + screen::Cell(caret_row, caret_column as u16);

    // helper to put a character on the screen
    let put = |character, cell: screen::Cell, screen: &mut screen::Screen| {
      use screen::Color::*;
      let (fg, bg) = if focused && cell != caret_cell { (Black, White) }
                     else                             { (White, Black) };
      screen.put(cell, character, fg, bg);
    };

    let screen::Size(rows, cols) = self.size;
    // draw line by line
    let mut row: u16 = 0;
    for chars in buffer.line_iter().from(self.scroll_line).take(rows as usize) {
      let line_offset = screen::Cell(row, 0) + position;
      // draw character by character
      let mut col = -(self.scroll_column as isize);
      for character in chars {
        if col >= cols as isize || character == '\n' { break }
        let char_width = character.width(false).unwrap_or(0) as isize;
        let end_col = col + char_width;
        if (col < 0 && end_col >= 0) || end_col > cols as isize {
          // blank out partially visible characters
          for col in cmp::max(0, col)..cmp::min(end_col, cols as isize) {
            put(' ', line_offset + screen::Cell(0, col as u16), screen);
          }
        }
        else if col >= 0 {
          put(character, line_offset + screen::Cell(0, col as u16), screen);
        }
        col += char_width;
      }
      // blank out the rest of the row if the line didn't fill it
      for col in cmp::max(0, col) as u16..cols {
        put(' ', line_offset + screen::Cell(0, col), screen);
      }
      row += 1;
    }
    // fill in the rest of the view below the buffer content
    for row in row..rows {
      let line_offset = screen::Cell(row, 0) + position;
      put(if self.scroll_column == 0 { '~' } else { ' ' }, line_offset, screen);
      for col in 1..cols {
        put(' ', line_offset + screen::Cell(0, col), screen);
      }
    }
  }
}

// sums up the widths of the characters before the given buffer column
fn buffer_to_screen_column(line: usize, column: usize, buffer: &buffer::Buffer)
    -> usize {
  buffer.line_iter().from(line).next().map(|chars|
    chars.take(column).map(|c| c.width(false).unwrap_or(0)).sum()).
  unwrap_or(0)
}

// scans a line, counting characters up to the given screen column
fn screen_to_buffer_column(row: usize, screen_column: usize,
    buffer: &buffer::Buffer) -> Option<usize> {
  buffer.line_iter().from(row).next().map(|chars|
    chars.filter(|&c| c != '\n').scan(0, |sum, c| {
      *sum += c.width(false).unwrap_or(0);
      Some(*sum) }).
    take_while(|&sum| sum <= screen_column).count())
}

#[cfg(test)]
mod test {
  use std::path::Path;

  use buffer;
  use screen;

  #[test]
  fn scroll_into_view_double_width() {
    let buffer = buffer::Buffer::open(
      &Path::new("tests/view/scroll_into_view_double_width.txt")).unwrap();
    let mut view = super::View::new();
    view.set_size(screen::Size(1, 15), &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 0);
    view.scroll_into_view(0, 12, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 3);
    view.scroll_into_view(0, 16, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 9);
    view.scroll_into_view(0, 3, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 6);
    view.scroll_into_view(3, 10, &buffer);
    assert_eq!(view.scroll_line, 3); assert_eq!(view.scroll_column, 6);
  }

  #[test]
  fn move_caret() {
    let buffer = buffer::Buffer::open(
      &Path::new("tests/view/hokey_pokey_caret.txt")).unwrap();
    let mut view = super::View::new();
    // move to empty line
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 1); assert_eq!(view.caret_column, 0);
    // move to end of double width character then back again
    view.caret_line = 3; view.caret_column = 3;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 4); assert_eq!(view.caret_column, 1);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 3); assert_eq!(view.caret_column, 3);
    // move to shorter lines then back again
    view.caret_line = 6; view.caret_column = 30;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 7); assert_eq!(view.caret_column, 14);
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 8); assert_eq!(view.caret_column, 20);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 6); assert_eq!(view.caret_column, 30);
    // move to shorter line, step sideways, then back again
    view.caret_line = 10; view.caret_column = 75;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 68);
    view.move_caret(super::CaretMovement::CharPrev, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 67);
    view.move_caret(super::CaretMovement::CharNext, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 68);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 10); assert_eq!(view.caret_column, 68);
    // move to end of line lacking newline
    view.caret_line = 13; view.caret_column = 34;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 14); assert_eq!(view.caret_column, 34);
    view.move_caret(super::CaretMovement::CharNext, &buffer);
    assert_eq!(view.caret_line, 14); assert_eq!(view.caret_column, 34);
  }
}
<commit_msg>Removed std::iter::AdditiveIterator<commit_after>/*
 * Copyright (c) 2015 Mathias Hällman
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::cmp;

use buffer;
use screen;

const MIN_VIEW_SIZE: u16 = 1;

#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(test, derive(Debug))]
pub enum CaretMovement {
  LineUp,
  LineDown,
  CharNext,
  CharPrev,
}

/*
 * View handles the caret and presentation of a buffer.
 * Scroll and size are both measured in screen cell coordinates. While the caret
 * is in buffer coordinates, the saved column is in screen coordinates.
 */
pub struct View {
  scroll_line: usize,
  scroll_column: usize,
  size: screen::Size,
  caret_line: usize,
  caret_column: usize,
  saved_column: Option<usize>,
}

impl View {
  pub fn new() -> View {
    View {
      scroll_line: 0,
      scroll_column: 0,
      size: screen::Size(MIN_VIEW_SIZE, MIN_VIEW_SIZE),
      caret_line: 0,
      caret_column: 0,
      saved_column: None,
    }
  }

  fn scroll_into_view(&mut self, line: usize, column: usize,
      buffer: &buffer::Buffer) {
    let screen::Size(rows, cols) = self.size;
    let rows = rows as usize;
    let cols = cols as usize;

    self.scroll_line =
      if line < self.scroll_line { line }
      else if line >= self.scroll_line + rows { line - rows + 1 }
      else { self.scroll_line };

    // make sure wider characters are scrolled in entirely as well
    let character = buffer.get_char_by_line_column(line, column).unwrap();
    let start = buffer_to_screen_column(line, column, buffer);
    let end = start + character.width(false).unwrap_or(1) - 1;
    self.scroll_column =
      if start < self.scroll_column { start }
      else if end >= self.scroll_column + cols { end - cols + 1 }
      else { self.scroll_column };
  }

  // Moves the caret and preserves its old screen space column when moving to a
  // line that can't have the caret at the same column.
  pub fn move_caret(&mut self, movement: CaretMovement,
      buffer: &buffer::Buffer) {
    let (line, column) = (self.caret_line, self.caret_column);
    let (new_line, new_column, new_saved_column) = match movement {
      CaretMovement::CharPrev =>
        (line, cmp::max(0, column as isize - 1) as usize, None),
      CaretMovement::CharNext => {
        let line_length = buffer.line_length(line).unwrap();
        let max_column = cmp::max(0, line_length as isize - 1) as usize;
        (line, cmp::min(max_column, column + 1), None)
      }
      CaretMovement::LineUp   =>
        if line == 0 { (line, column, self.saved_column) }
        else { self.vertical_caret_movement(line, line - 1, buffer) },
      CaretMovement::LineDown => {
        let max_line = cmp::max(0, buffer.num_lines() as isize - 1) as usize;
        if line == max_line { (line, column, self.saved_column) }
        else { self.vertical_caret_movement(line, line + 1, buffer) }
      }
    };
    if line != new_line || column != new_column {
      self.caret_line = new_line;
      self.caret_column = new_column;
      self.saved_column = new_saved_column;
      self.scroll_into_view(new_line, new_column, buffer);
    }
  }

  // helper function to move_caret, restricts the caret column to valid
  // character positions in screen space
  fn vertical_caret_movement(&self, from_line: usize, to_line: usize,
      buffer: &buffer::Buffer) -> (usize, usize, Option<usize>) {
    let to_line_length = buffer.line_length(to_line).unwrap();
    let to_line_screen_length =
      buffer_to_screen_column(to_line, to_line_length, buffer);
    let max_column = cmp::max(0, to_line_screen_length as isize - 1) as usize;
    let desired_column = self.saved_column.unwrap_or(
      buffer_to_screen_column(from_line, self.caret_column, buffer));
    let screen_column = cmp::min(max_column, desired_column);
    let buffer_column =
      screen_to_buffer_column(to_line, screen_column, buffer).unwrap();
    (to_line, buffer_column,
      if buffer_column == desired_column { None } else { Some(desired_column) })
  }

  pub fn set_size(&mut self, size: screen::Size, buffer: &buffer::Buffer) {
    let screen::Size(rows, cols) = size;
    assert!(rows >= MIN_VIEW_SIZE && cols >= MIN_VIEW_SIZE);
    self.size = size;
    let (line, column) = (self.caret_line, self.caret_column);
    self.scroll_into_view(line, column, buffer);
  }

  #[cfg(not(test))]
  pub fn draw(&self, buffer: &buffer::Buffer, focused: bool,
      position: screen::Cell, screen: &mut screen::Screen) {
    // calculate caret screen position
    let caret_row = (self.caret_line - self.scroll_line) as u16;
    let caret_column = buffer_to_screen_column(
      self.caret_line, self.caret_column, buffer) - self.scroll_column;
    let caret_cell = position + screen::Cell(caret_row, caret_column as u16);

    // helper to put a character on the screen
    let put = |character, cell: screen::Cell, screen: &mut screen::Screen| {
      use screen::Color::*;
      let (fg, bg) = if focused && cell != caret_cell { (Black, White) }
                     else                             { (White, Black) };
      screen.put(cell, character, fg, bg);
    };

    let screen::Size(rows, cols) = self.size;
    // draw line by line
    let mut row: u16 = 0;
    for chars in buffer.line_iter().from(self.scroll_line).take(rows as usize) {
      let line_offset = screen::Cell(row, 0) + position;
      // draw character by character
      let mut col = -(self.scroll_column as isize);
      for character in chars {
        if col >= cols as isize || character == '\n' { break }
        let char_width = character.width(false).unwrap_or(0) as isize;
        let end_col = col + char_width;
        if (col < 0 && end_col >= 0) || end_col > cols as isize {
          // blank out partially visible characters
          for col in cmp::max(0, col)..cmp::min(end_col, cols as isize) {
            put(' ', line_offset + screen::Cell(0, col as u16), screen);
          }
        }
        else if col >= 0 {
          put(character, line_offset + screen::Cell(0, col as u16), screen);
        }
        col += char_width;
      }
      // blank out the rest of the row if the line didn't fill it
      for col in cmp::max(0, col) as u16..cols {
        put(' ', line_offset + screen::Cell(0, col), screen);
      }
      row += 1;
    }
    // fill in the rest of the view below the buffer content
    for row in row..rows {
      let line_offset = screen::Cell(row, 0) + position;
      put(if self.scroll_column == 0 { '~' } else { ' ' }, line_offset, screen);
      for col in 1..cols {
        put(' ', line_offset + screen::Cell(0, col), screen);
      }
    }
  }
}

// sums up the widths of the characters before the given buffer column
fn buffer_to_screen_column(line: usize, column: usize, buffer: &buffer::Buffer)
    -> usize {
  buffer.line_iter().from(line).next().map(|chars|
    chars.take(column).map(|c| c.width(false).unwrap_or(0)).sum()).
  unwrap_or(0)
}

// scans a line, counting characters up to the given screen column
fn screen_to_buffer_column(row: usize, screen_column: usize,
    buffer: &buffer::Buffer) -> Option<usize> {
  buffer.line_iter().from(row).next().map(|chars|
    chars.filter(|&c| c != '\n').scan(0, |sum, c| {
      *sum += c.width(false).unwrap_or(0);
      Some(*sum) }).
    take_while(|&sum| sum <= screen_column).count())
}

#[cfg(test)]
mod test {
  use std::path::Path;

  use buffer;
  use screen;

  #[test]
  fn scroll_into_view_double_width() {
    let buffer = buffer::Buffer::open(
      &Path::new("tests/view/scroll_into_view_double_width.txt")).unwrap();
    let mut view = super::View::new();
    view.set_size(screen::Size(1, 15), &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 0);
    view.scroll_into_view(0, 12, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 3);
    view.scroll_into_view(0, 16, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 9);
    view.scroll_into_view(0, 3, &buffer);
    assert_eq!(view.scroll_line, 0); assert_eq!(view.scroll_column, 6);
    view.scroll_into_view(3, 10, &buffer);
    assert_eq!(view.scroll_line, 3); assert_eq!(view.scroll_column, 6);
  }

  #[test]
  fn move_caret() {
    let buffer = buffer::Buffer::open(
      &Path::new("tests/view/hokey_pokey_caret.txt")).unwrap();
    let mut view = super::View::new();
    // move to empty line
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 1); assert_eq!(view.caret_column, 0);
    // move to end of double width character then back again
    view.caret_line = 3; view.caret_column = 3;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 4); assert_eq!(view.caret_column, 1);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 3); assert_eq!(view.caret_column, 3);
    // move to shorter lines then back again
    view.caret_line = 6; view.caret_column = 30;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 7); assert_eq!(view.caret_column, 14);
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 8); assert_eq!(view.caret_column, 20);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 6); assert_eq!(view.caret_column, 30);
    // move to shorter line, step sideways, then back again
    view.caret_line = 10; view.caret_column = 75;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 68);
    view.move_caret(super::CaretMovement::CharPrev, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 67);
    view.move_caret(super::CaretMovement::CharNext, &buffer);
    assert_eq!(view.caret_line, 11); assert_eq!(view.caret_column, 68);
    view.move_caret(super::CaretMovement::LineUp, &buffer);
    assert_eq!(view.caret_line, 10); assert_eq!(view.caret_column, 68);
    // move to end of line lacking newline
    view.caret_line = 13; view.caret_column = 34;
    view.move_caret(super::CaretMovement::LineDown, &buffer);
    assert_eq!(view.caret_line, 14); assert_eq!(view.caret_column, 34);
    view.move_caret(super::CaretMovement::CharNext, &buffer);
    assert_eq!(view.caret_line, 14); assert_eq!(view.caret_column, 34);
  }
}
<|endoftext|>


<commit_before>use std::string::String;
use std::fs::File;
use std::io::{BufReader, Read};
use std::collections::HashMap;
use std::borrow::Cow;

use zip::read::{ZipFile, ZipArchive};
use zip::result::ZipError;
use quick_xml::{XmlReader, Event, AsStr};
use encoding::{Encoding, DecoderTrap};
use encoding::all::UTF_16LE;

use {DataType, ExcelReader, Range, CellErrorType};
use vba::VbaProject;
use utils::{read_u32, read_usize, read_slice};
use errors::*;

pub struct Xlsb {
    zip: ZipArchive<File>,
}

impl Xlsb {
    fn iter<'a>(&'a mut self, path: &str) -> Result<RecordIter<'a>> {
        match self.zip.by_name(path) {
            Ok(f) => Ok(RecordIter { r: BufReader::new(f), b: [0] }),
            Err(ZipError::FileNotFound) => Err(format!("file {} does not exist", path).into()),
            Err(e) => Err(e.into()),
        }
    }
}

impl ExcelReader for Xlsb {

    fn new(f: File) -> Result<Self> {
        Ok(Xlsb { zip: try!(ZipArchive::new(f)) })
    }

    fn has_vba(&mut self) -> bool {
        self.zip.by_name("xl/vbaProject.bin").is_ok()
    }

    fn vba_project(&mut self) -> Result<Cow<VbaProject>> {
        let mut f = try!(self.zip.by_name("xl/vbaProject.bin"));
        let len = f.size() as usize;
        VbaProject::new(&mut f, len).map(|v| Cow::Owned(v))
    }

    /// MS-XLSB
    fn read_relationships(&mut self) -> Result<HashMap<Vec<u8>, String>> {
        let mut relationships = HashMap::new();
        match self.zip.by_name("xl/_rels/workbook.bin.rels") {
            Ok(f) => {
                let xml = XmlReader::from_reader(BufReader::new(f))
                    .with_check(false)
                    .trim_text(false);

                for res_event in xml {
                    match res_event {
                        Ok(Event::Start(ref e)) if e.name() == b"Relationship" => {
                            let mut id = Vec::new();
                            let mut target = String::new();
                            for a in e.attributes() {
                                match try!(a) {
                                    (b"Id", v) => id.extend_from_slice(v),
                                    (b"Target", v) => target = try!(v.as_str()).to_string(),
                                    _ => (),
                                }
                            }
                            relationships.insert(id, target);
                        }
                        Err(e) => return Err(e.into()),
                        _ => (),
                    }
                }
            },
            Err(ZipError::FileNotFound) => (),
            Err(e) => return Err(e.into()),
        }
        Ok(relationships)
    }

    /// MS-XLSB 2.1.7.45
    fn read_shared_strings(&mut self) -> Result<Vec<String>> {
        let mut iter = match self.iter("xl/sharedStrings.bin") {
            Ok(iter) => iter,
            Err(_) => return Ok(Vec::new()), // it is fine if path does not exists
        };
        let mut buf = vec![0; 1024];

        let _ = try!(iter.next_skip_blocks(0x009F, &[], &mut buf)); // BrtBeginSst
        let len = read_usize(&buf[4..8]);
        let mut strings = Vec::with_capacity(len);

        // BrtSSTItems
        for _ in 0..len {
            let _ = try!(iter.next_skip_blocks(0x0013, &[
                                               (0x0023, Some(0x0024)) // future
                                               ], &mut buf)); // BrtSSTItem
            strings.push(try!(wide_str(&buf[1..])));
        }
        Ok(strings)
    }

    /// MS-XLSB 2.1.7.61
    fn read_sheets_names(&mut self, relationships: &HashMap<Vec<u8>, String>)
        -> Result<HashMap<String, String>>
    {
        let mut iter = try!(self.iter("xl/workbook.bin"));
        let mut buf = vec![0; 1024];

        // BrtBeginBundleShs
        let _ = try!(iter.next_skip_blocks(0x008F, &[
                                          (0x0083, None),         // BrtBeginBook
                                          (0x0080, None),         // BrtFileVersion
                                          (0x0099, None),         // BrtWbProp
                                          (0x02A4, Some(0x0224)), // File Sharing
                                          (0x0025, Some(0x0026)), // AC blocks
                                          (0x02A5, Some(0x0216)), // Book protection(iso)
                                          (0x0087, Some(0x0088)), // BOOKVIEWS
                                          ], &mut buf));
        let mut sheets = HashMap::new();
        loop {
            match try!(iter.read_type()) {
                0x0090 => return Ok(sheets), // BrtEndBundleShs
                0x009C => (), // BrtBundleSh
                typ => return Err(format!("Expecting end of sheet, got {:x}", typ).into()),
            }
            let len = try!(iter.fill_buffer(&mut buf));
            let rel_len = read_u32(&buf[8..len]);
            if rel_len != 0xFFFFFFFF {
                let rel_len = rel_len as usize * 2;
                let relid = &buf[12..12 + rel_len];
                // converts utf16le to utf8 for HashMap search
                let relid = try!(UTF_16LE.decode(relid, DecoderTrap::Ignore).map_err(|e| e.to_string()));
                let path = format!("xl/{}", relationships[relid.as_bytes()]);
                let name = try!(wide_str(&buf[12 + rel_len..len]));
                sheets.insert(name, path);
            }
        }
    }

    /// MS-XLSB 2.1.7.62
    fn read_worksheet_range(&mut self, path: &str, strings: &[String]) -> Result<Range> {
        let mut iter = try!(self.iter(path));
        let mut buf = vec![0; 1024];

        // BrtWsDim
        let _ = try!(iter.next_skip_blocks(0x0094, &[
                                           (0x0081, None), // BrtBeginSheet
                                           (0x0093, None), // BrtWsProp
                                           ], &mut buf));
        let (start, end) = parse_dimensions(&buf[..16]);
        let mut range = Range::new(start, end);

        if range.is_empty() {
            return Ok(range);
        }

        // BrtBeginSheetData
        let _ = try!(iter.next_skip_blocks(0x0091, &[
                                          (0x0085, Some(0x0086)), // Views
                                          (0x0025, Some(0x0026)), // AC blocks
                                          (0x01E5, None),         // BrtWsFmtInfo
                                          (0x0186, Some(0x0187)), // Col Infos
                                          ], &mut buf));

        // Initialization: first BrtRowHdr
        let mut typ: u16;
        let mut row = 0u32;

        // loop until end of sheet
        loop {
            typ = try!(iter.read_type());
            let _ = try!(iter.fill_buffer(&mut buf));

            let value = match typ {
                0x0001 => continue, // DataType::Empty, // BrtCellBlank
                0x0002 => { // BrtCellRk MS-XLSB 2.5.122
                    let d100 = (buf[8] & 1) != 0;
                    let is_int = (buf[8] & 2) != 0;
                    buf[8] &= 0xFC;
                    if is_int {
                        let v = (read_slice::<i32>(&buf[8..12]) >> 2) as i64;
                        DataType::Int( if d100 { v/100 } else { v })
                    } else {
                        let mut v = [0u8; 8];
                        v[4..].copy_from_slice(&buf[8..12]);
                        let v = read_slice(&v);
                        DataType::Float( if d100 { v/100.0 } else { v })
                    }
                },
                0x0003 => { // BrtCellError
                    DataType::Error(match buf[8] {
                        0x00 => CellErrorType::Null,
                        0x07 => CellErrorType::Div0,
                        0x0F => CellErrorType::Value,
                        0x17 => CellErrorType::Ref,
                        0x1D => CellErrorType::Name,
                        0x24 => CellErrorType::Num,
                        0x2A => CellErrorType::NA,
                        0x2B => CellErrorType::GettingData,
                        c => return Err(format!("Unrecognised cell error code 0x{:x}", c).into()),
                    })
                },
                0x0004 => DataType::Bool(buf[8] != 0), // BrtCellBool
                0x0005 => DataType::Float(read_slice(&buf[8..16])), // BrtCellReal
                0x0006 => DataType::String(try!(wide_str(&buf[8..]))), // BrtCellSt
                0x0007 => { // BrtCellIsst
                    let isst = read_usize(&buf[8..12]);
                    DataType::String(strings[isst].clone())
                },
                0x0000 => { // BrtRowHdr
                    row = read_u32(&buf);
                    if row > 0x00100000 {
                        return Ok(range); // invalid row
                    }
                    continue;
                },
                0x0092 => return Ok(range),  // BrtEndSheetData
                _ => continue,  // anything else, ignore and try next, without changing idx
            };

            let col = read_u32(&buf);
            try!(range.set_value((row, col), value));
        }
    }
}

struct RecordIter<'a> {
    b: [u8; 1],
    r: BufReader<ZipFile<'a>>,
}

impl<'a> RecordIter<'a> {

    fn read_u8(&mut self) -> Result<u8> {
        try!(self.r.read_exact(&mut self.b));
        Ok(self.b[0])
    }

    fn read_type(&mut self) -> Result<u16> {
        let b = try!(self.read_u8());
        let typ = if (b & 0x80) == 0x80 {
            (b & 0x7F) as u16 + (((try!(self.read_u8()) & 0x7F) as u16)<<7)
        } else {
            b as u16
        };
        Ok(typ)
    }

    fn fill_buffer(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
        let mut b = try!(self.read_u8());
        let mut len = b as usize;
        for i in 0..3 {
            if (b & 0x80) == 0 { break; }
            b = try!(self.read_u8());
            len += (b as usize) << (7 * i);
        }
        if buf.len() < len { *buf = vec![0; len]; }

        let _ = try!(self.r.read_exact(&mut buf[..len]));
        Ok(len)
    }

    /// Reads next type, and discard blocks between `start` and `end`
    fn next_skip_blocks(&mut self, record_type: u16, bounds: &[(u16, Option<u16>)],
                       buf: &mut Vec<u8>) -> Result<usize>
    {
        let mut end = None;
        loop {
            let typ = try!(self.read_type());
            match end {
                Some(e) if e == typ        => end = None,
                Some(_)                    => (),
                None if typ == record_type => return self.fill_buffer(buf),
                None                       => {
                    match bounds.iter().position(|b| b.0 == typ) {
                        Some(i) => end = bounds[i].1,
                        None => return Err(format!("Unexpected record after block: \
                            expecting 0x{:x} found 0x{:x}", record_type, typ).into()),
                    }
                },
            }
            let _ = try!(self.fill_buffer(buf));
        }
    }

}

fn wide_str(buf: &[u8]) -> Result<String> {
    let len = read_usize(buf);
    let s = &buf[4..4 + len * 2];
    UTF_16LE.decode(s, DecoderTrap::Ignore).map_err(|e| e.to_string().into())
}

fn parse_dimensions(buf: &[u8]) -> ((u32, u32), (u32, u32)) {
    ((read_u32(&buf[0..4]), read_u32(&buf[8..12])),
     (read_u32(&buf[4..8]), read_u32(&buf[12..16])))
}
<commit_msg>[xlsb] fix record length calculation<commit_after>use std::string::String;
use std::fs::File;
use std::io::{BufReader, Read};
use std::collections::HashMap;
use std::borrow::Cow;

use zip::read::{ZipFile, ZipArchive};
use zip::result::ZipError;
use quick_xml::{XmlReader, Event, AsStr};
use encoding::{Encoding, DecoderTrap};
use encoding::all::UTF_16LE;

use {DataType, ExcelReader, Range, CellErrorType};
use vba::VbaProject;
use utils::{read_u32, read_usize, read_slice};
use errors::*;

pub struct Xlsb {
    zip: ZipArchive<File>,
}

impl Xlsb {
    fn iter<'a>(&'a mut self, path: &str) -> Result<RecordIter<'a>> {
        match self.zip.by_name(path) {
            Ok(f) => Ok(RecordIter { r: BufReader::new(f), b: [0] }),
            Err(ZipError::FileNotFound) => Err(format!("file {} does not exist", path).into()),
            Err(e) => Err(e.into()),
        }
    }
}

impl ExcelReader for Xlsb {

    fn new(f: File) -> Result<Self> {
        Ok(Xlsb { zip: try!(ZipArchive::new(f)) })
    }

    fn has_vba(&mut self) -> bool {
        self.zip.by_name("xl/vbaProject.bin").is_ok()
    }

    fn vba_project(&mut self) -> Result<Cow<VbaProject>> {
        let mut f = try!(self.zip.by_name("xl/vbaProject.bin"));
        let len = f.size() as usize;
        VbaProject::new(&mut f, len).map(|v| Cow::Owned(v))
    }

    /// MS-XLSB
    fn read_relationships(&mut self) -> Result<HashMap<Vec<u8>, String>> {
        let mut relationships = HashMap::new();
        match self.zip.by_name("xl/_rels/workbook.bin.rels") {
            Ok(f) => {
                let xml = XmlReader::from_reader(BufReader::new(f))
                    .with_check(false)
                    .trim_text(false);

                for res_event in xml {
                    match res_event {
                        Ok(Event::Start(ref e)) if e.name() == b"Relationship" => {
                            let mut id = Vec::new();
                            let mut target = String::new();
                            for a in e.attributes() {
                                match try!(a) {
                                    (b"Id", v) => id.extend_from_slice(v),
                                    (b"Target", v) => target = try!(v.as_str()).to_string(),
                                    _ => (),
                                }
                            }
                            relationships.insert(id, target);
                        }
                        Err(e) => return Err(e.into()),
                        _ => (),
                    }
                }
            },
            Err(ZipError::FileNotFound) => (),
            Err(e) => return Err(e.into()),
        }
        Ok(relationships)
    }

    /// MS-XLSB 2.1.7.45
    fn read_shared_strings(&mut self) -> Result<Vec<String>> {
        let mut iter = match self.iter("xl/sharedStrings.bin") {
            Ok(iter) => iter,
            Err(_) => return Ok(Vec::new()), // it is fine if path does not exists
        };
        let mut buf = vec![0; 1024];

        let _ = try!(iter.next_skip_blocks(0x009F, &[], &mut buf)); // BrtBeginSst
        let len = read_usize(&buf[4..8]);
        let mut strings = Vec::with_capacity(len);

        // BrtSSTItems
        let mut typ = 0;
        while len > strings.len() && typ != 0x00A0 {
            typ = try!(iter.read_type());
            let _ = try!(iter.fill_buffer(&mut buf));
            if typ == 0x0013 {
                strings.push(try!(wide_str(&buf[1..])));
            }
        }
        Ok(strings)
    }

    /// MS-XLSB 2.1.7.61
    fn read_sheets_names(&mut self, relationships: &HashMap<Vec<u8>, String>)
        -> Result<HashMap<String, String>>
    {
        let mut iter = try!(self.iter("xl/workbook.bin"));
        let mut buf = vec![0; 1024];

        // BrtBeginBundleShs
        let _ = try!(iter.next_skip_blocks(0x008F, &[
                                          (0x0083, None),         // BrtBeginBook
                                          (0x0080, None),         // BrtFileVersion
                                          (0x0099, None),         // BrtWbProp
                                          (0x02A4, Some(0x0224)), // File Sharing
                                          (0x0025, Some(0x0026)), // AC blocks
                                          (0x02A5, Some(0x0216)), // Book protection(iso)
                                          (0x0087, Some(0x0088)), // BOOKVIEWS
                                          ], &mut buf));
        let mut sheets = HashMap::new();
        loop {
            match try!(iter.read_type()) {
                0x0090 => return Ok(sheets), // BrtEndBundleShs
                0x009C => (), // BrtBundleSh
                typ => return Err(format!("Expecting end of sheet, got {:x}", typ).into()),
            }
            let len = try!(iter.fill_buffer(&mut buf));
            let rel_len = read_u32(&buf[8..len]);
            if rel_len != 0xFFFFFFFF {
                let rel_len = rel_len as usize * 2;
                let relid = &buf[12..12 + rel_len];
                // converts utf16le to utf8 for HashMap search
                let relid = try!(UTF_16LE.decode(relid, DecoderTrap::Ignore).map_err(|e| e.to_string()));
                let path = format!("xl/{}", relationships[relid.as_bytes()]);
                let name = try!(wide_str(&buf[12 + rel_len..len]));
                sheets.insert(name, path);
            }
        }
    }

    /// MS-XLSB 2.1.7.62
    fn read_worksheet_range(&mut self, path: &str, strings: &[String]) -> Result<Range> {
        let mut iter = try!(self.iter(path));
        let mut buf = vec![0; 1024];

        // BrtWsDim
        let _ = try!(iter.next_skip_blocks(0x0094, &[
                                           (0x0081, None), // BrtBeginSheet
                                           (0x0093, None), // BrtWsProp
                                           ], &mut buf));
        let (start, end) = parse_dimensions(&buf[..16]);
        let mut range = Range::new(start, end);

        if range.is_empty() {
            return Ok(range);
        }

        // BrtBeginSheetData
        let _ = try!(iter.next_skip_blocks(0x0091, &[
                                          (0x0085, Some(0x0086)), // Views
                                          (0x0025, Some(0x0026)), // AC blocks
                                          (0x01E5, None),         // BrtWsFmtInfo
                                          (0x0186, Some(0x0187)), // Col Infos
                                          ], &mut buf));

        // Initialization: first BrtRowHdr
        let mut typ: u16;
        let mut row = 0u32;

        // loop until end of sheet
        loop {
            typ = try!(iter.read_type());
            let _ = try!(iter.fill_buffer(&mut buf));

            let value = match typ {
                0x0001 => continue, // DataType::Empty, // BrtCellBlank
                0x0002 => { // BrtCellRk MS-XLSB 2.5.122
                    let d100 = (buf[8] & 1) != 0;
                    let is_int = (buf[8] & 2) != 0;
                    buf[8] &= 0xFC;
                    if is_int {
                        let v = (read_slice::<i32>(&buf[8..12]) >> 2) as i64;
                        DataType::Int( if d100 { v/100 } else { v })
                    } else {
                        let mut v = [0u8; 8];
                        v[4..].copy_from_slice(&buf[8..12]);
                        let v = read_slice(&v);
                        DataType::Float( if d100 { v/100.0 } else { v })
                    }
                },
                0x0003 => { // BrtCellError
                    DataType::Error(match buf[8] {
                        0x00 => CellErrorType::Null,
                        0x07 => CellErrorType::Div0,
                        0x0F => CellErrorType::Value,
                        0x17 => CellErrorType::Ref,
                        0x1D => CellErrorType::Name,
                        0x24 => CellErrorType::Num,
                        0x2A => CellErrorType::NA,
                        0x2B => CellErrorType::GettingData,
                        c => return Err(format!("Unrecognised cell error code 0x{:x}", c).into()),
                    })
                },
                0x0004 => DataType::Bool(buf[8] != 0), // BrtCellBool
                0x0005 => DataType::Float(read_slice(&buf[8..16])), // BrtCellReal
                0x0006 => DataType::String(try!(wide_str(&buf[8..]))), // BrtCellSt
                0x0007 => { // BrtCellIsst
                    let isst = read_usize(&buf[8..12]);
                    DataType::String(strings[isst].clone())
                },
                0x0000 => { // BrtRowHdr
                    row = read_u32(&buf);
                    if row > 0x00100000 {
                        return Ok(range); // invalid row
                    }
                    continue;
                },
                0x0092 => return Ok(range),  // BrtEndSheetData
                _ => continue,  // anything else, ignore and try next, without changing idx
            };

            let col = read_u32(&buf);
            try!(range.set_value((row, col), value));
        }
    }
}

struct RecordIter<'a> {
    b: [u8; 1],
    r: BufReader<ZipFile<'a>>,
}

impl<'a> RecordIter<'a> {

    fn read_u8(&mut self) -> Result<u8> {
        try!(self.r.read_exact(&mut self.b));
        Ok(self.b[0])
    }

    fn read_type(&mut self) -> Result<u16> {
        let b = try!(self.read_u8());
        let typ = if (b & 0x80) == 0x80 {
            (b & 0x7F) as u16 + (((try!(self.read_u8()) & 0x7F) as u16)<<7)
        } else {
            b as u16
        };
        Ok(typ)
    }

    fn fill_buffer(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
        let mut b = try!(self.read_u8());
        let mut len = (b & 0x7F) as usize;
        for i in 1..4 {
            if (b & 0x80) == 0 { break; }
            b = try!(self.read_u8());
            len += ((b & 0x7F) as usize) << (7 * i);
        }
        if buf.len() < len { *buf = vec![0; len]; }

        let _ = try!(self.r.read_exact(&mut buf[..len]));
        Ok(len)
    }

    /// Reads next type, and discard blocks between `start` and `end`
    fn next_skip_blocks(&mut self, record_type: u16, bounds: &[(u16, Option<u16>)],
                       buf: &mut Vec<u8>) -> Result<usize>
    {
        let mut end = None;
        loop {
            let typ = try!(self.read_type());
            match end {
                Some(e) if e == typ        => end = None,
                Some(_)                    => (),
                None if typ == record_type => return self.fill_buffer(buf),
                None                       => {
                    match bounds.iter().position(|b| b.0 == typ) {
                        Some(i) => end = bounds[i].1,
                        None => return Err(format!("Unexpected record after block: \
                            expecting 0x{:x} found 0x{:x}", record_type, typ).into()),
                    }
                },
            }
            let _ = try!(self.fill_buffer(buf));
        }
    }

}

fn wide_str(buf: &[u8]) -> Result<String> {
    let len = read_u32(buf) as usize;
    if buf.len() < 4 + len * 2 {
        return Err(format!("Wide string length ({}) exceeds buffer length ({})",
                           4 + len * 2, buf.len()).into());
    }
    let s = &buf[4..4 + len * 2];
    UTF_16LE.decode(s, DecoderTrap::Ignore).map_err(|e| e.to_string().into())
}

fn parse_dimensions(buf: &[u8]) -> ((u32, u32), (u32, u32)) {
    ((read_u32(&buf[0..4]), read_u32(&buf[8..12])),
     (read_u32(&buf[4..8]), read_u32(&buf[12..16])))
}
<|endoftext|>


<commit_before>//! Module: czmq-zsys

use {czmq_sys, RawInterface, Result};
use error::{Error, ErrorKind};
use std::{error, fmt, ptr};
use std::os::raw::c_void;
use std::sync::{Once, ONCE_INIT};
use zsock::{ZSock, ZSockType};

static INIT_ZSYS: Once = ONCE_INIT;

pub struct ZSys;

impl ZSys {
    // Each new ZSock calls zsys_init(), which is a non-threadsafe
    // fn. To mitigate the race condition, wrap it in a Once struct.
    pub fn init() {
        INIT_ZSYS.call_once(|| {
            unsafe { czmq_sys::zsys_init() };
        });
    }

    /// Create a pipe, which consists of two PAIR sockets connected
    /// over inproc.
    pub fn create_pipe() -> Result<(ZSock, ZSock)> {
        let mut backend_raw = ZSock::new(ZSockType::PAIR).into_raw() as *mut czmq_sys::zsock_t;
        let frontend_raw = unsafe { czmq_sys::zsys_create_pipe(&mut backend_raw) };

        if frontend_raw == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZSysError::CreatePipe))
        } else {
            Ok((ZSock::from_raw(frontend_raw as *mut c_void, true), ZSock::from_raw(backend_raw as *mut c_void, true)))
        }
    }
}

#[derive(Debug)]
pub enum ZSysError {
    CreatePipe,
}

impl fmt::Display for ZSysError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZSysError::CreatePipe => write!(f, "Could not create pipe"),
        }
    }
}

impl error::Error for ZSysError {
    fn description(&self) -> &str {
        match *self {
            ZSysError::CreatePipe => "Could not create pipe",
        }
    }
}

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

    #[test]
    fn test_create_pipe() {
        ZSys::init();

        let (frontend, backend) = ZSys::create_pipe().unwrap();
        frontend.send_str("I changed my iPod’s name to Titanic...now it’s syncing.").unwrap();
        assert_eq!(backend.recv_str().unwrap().unwrap(), "I changed my iPod’s name to Titanic...now it’s syncing.");
    }
}
<commit_msg>Fixed bug where ZSys::create_pipe() created a ghost socket...then orphaned it :(<commit_after>//! Module: czmq-zsys

use {czmq_sys, RawInterface, Result};
use error::{Error, ErrorKind};
use std::{error, fmt, ptr};
use std::os::raw::c_void;
use std::sync::{Once, ONCE_INIT};
use zsock::ZSock;

static INIT_ZSYS: Once = ONCE_INIT;

pub struct ZSys;

impl ZSys {
    // Each new ZSock calls zsys_init(), which is a non-threadsafe
    // fn. To mitigate the race condition, wrap it in a Once struct.
    pub fn init() {
        INIT_ZSYS.call_once(|| {
            unsafe { czmq_sys::zsys_init() };
        });
    }

    /// Create a pipe, which consists of two PAIR sockets connected
    /// over inproc.
    pub fn create_pipe() -> Result<(ZSock, ZSock)> {
        let mut backend_raw: *mut czmq_sys::zsock_t = ptr::null_mut();
        let frontend_raw = unsafe { czmq_sys::zsys_create_pipe(&mut backend_raw) };

        if frontend_raw == ptr::null_mut() {
            Err(Error::new(ErrorKind::NullPtr, ZSysError::CreatePipe))
        } else {
            Ok((ZSock::from_raw(frontend_raw as *mut c_void, true), ZSock::from_raw(backend_raw as *mut c_void, true)))
        }
    }
}

#[derive(Debug)]
pub enum ZSysError {
    CreatePipe,
}

impl fmt::Display for ZSysError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ZSysError::CreatePipe => write!(f, "Could not create pipe"),
        }
    }
}

impl error::Error for ZSysError {
    fn description(&self) -> &str {
        match *self {
            ZSysError::CreatePipe => "Could not create pipe",
        }
    }
}

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

    #[test]
    fn test_create_pipe() {
        ZSys::init();

        let (frontend, backend) = ZSys::create_pipe().unwrap();
        frontend.send_str("I changed my iPod’s name to Titanic...now it’s syncing.").unwrap();
        assert_eq!(backend.recv_str().unwrap().unwrap(), "I changed my iPod’s name to Titanic...now it’s syncing.");
        backend.send_str("My family laughed when I told them I was going to be a comedian. Well...they aren't laughing now!").unwrap();
        assert_eq!(frontend.recv_str().unwrap().unwrap(), "My family laughed when I told them I was going to be a comedian. Well...they aren't laughing now!");
    }
}
<|endoftext|>


<commit_before>// In heavy WIP
use std::fs::File;
use std::io::Read;
use std::path::Path;
use toml;

use error::PackageError;
use repository::RepositoryUrlList;

/// Represents the config file.
#[derive(Deserialize)]
pub struct Config {
    pub buffer_size: Option<u64>,
    pub config_path: Option<String>,
    pub download_path: Option<String>,
    pub unpack_path: Option<String>,
    pub repository: Option<RepositoryUrlList>,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            buffer_size: Some(65536),
            config_path: Some(String::from("~/.wiz/config/")),
            download_path: Some(String::from("~/.wiz/downloads/")),
            unpack_path: Some(String::from("~/.wiz/downloads/unpacked/")),
            repository: Some(RepositoryUrlList(Vec::new())),
        }
    }
}

/// Internal macro, to ease the implementation of `fill_with_default`.
macro_rules! set_on_none {
    ($dest:ident, $key:ident, $value:expr) => (
        match $dest {
            Self { $key: ref mut x, .. } => {
                match x {
                    &mut Some(_) => {},
                    &mut None => *x = $value,
                }
            }
        }

        // Wait for RFC-2086 to be implemented first.
        /*
            [allow(irrefutable_let_pattern)]
            if let Config { $key: mut x, .. } = $dest {
                if let None = x { x = $value }
            }
        */
    );
}

impl Config {
    /// Read the config file from the config path specified in `path`.
    /// If the config file is read & parsed properly, it should return
    /// a `Config`.
    pub fn read_from(path: &Path) -> Result<Self, PackageError> {
        // Read the config file into a string
        let mut config = File::open(path)?;
        let mut content = String::new();
        config.read_to_string(&mut content)?;

        // Try parsing the string, and convert it into a `Config`.
        let config = content.parse::<toml::Value>()?;
        let config = config.try_into::<Self>()?;
        Ok(config)
    }

    /// Read the config file from the default config path.
    /// If the config file is read & parsed properly, it should return
    /// a `Config`.
    pub fn read_from_default() -> Result<Self, PackageError> {
        if let Self { config_path: Some(path), .. } = Self::default() {
            Self::read_from(Path::new(&path))
        } else {
            // This should never, ever, happen.
            Err(PackageError::Parsing(
                String::from("Unable to get config_path from the default cfg.")
            ))
        }
    }

    /// This function sets the None(s) in the config, to the default values.
    pub fn fill_with_default(mut self) -> Self {
        // Destructuring the default configs into individual variables.
        let Self {
            buffer_size: buf_size,
            config_path: conf_path,
            download_path: dl_path,
            unpack_path: unpk_path,
            repository: repo,
        } = Self::default();

        // If there are None(s), set them to the default value.
        set_on_none!(self, buffer_size, buf_size);
        set_on_none!(self, config_path, conf_path);
        set_on_none!(self, download_path, dl_path);
        set_on_none!(self, unpack_path, unpk_path);
        set_on_none!(self, repository, repo);

        self
    }
}
<commit_msg>Allow multiple config files to be read<commit_after>// In heavy WIP
use std::fs::{ self, File };
use std::io::Read;
use std::path::Path;
use toml;

use error::PackageError;
use repository::RepositoryUrlList;

/// Represents the config file.
#[derive(Deserialize)]
pub struct Config {
    pub buffer_size: Option<u64>,
    pub config_path: Option<String>,
    pub download_path: Option<String>,
    pub unpack_path: Option<String>,
    pub repository: Option<RepositoryUrlList>,
}

impl Default for Config {
    fn default() -> Self {
        Self {
            buffer_size: Some(65536),
            config_path: Some(String::from("~/.wiz/config/")),
            download_path: Some(String::from("~/.wiz/downloads/")),
            unpack_path: Some(String::from("~/.wiz/downloads/unpacked/")),
            repository: Some(RepositoryUrlList(Vec::new())),
        }
    }
}

/// Internal macro, to ease the implementation of `fill_with_default`.
macro_rules! set_on_none {
    ($dest:ident, $key:ident, $value:expr) => (
        match $dest {
            Self { $key: ref mut x, .. } => {
                match x {
                    &mut Some(_) => {},
                    &mut None => *x = $value,
                }
            }
        }

        // Wait for RFC-2086 to be implemented first.
        /*
            [allow(irrefutable_let_pattern)]
            if let Config { $key: mut x, .. } = $dest {
                if let None = x { x = $value }
            }
        */
    );
}

impl Config {
    /// Read the config file from the config path specified in `path`.
    /// If the config file is read & parsed properly, it should return
    /// a `Config`.
    pub fn read_from<P: AsRef<Path>>(path: P) -> Result<Self, PackageError> {
        let path = path.as_ref(); // maybe I should replace &Path with these

        let mut content = String::new();

        // Check whether there are tons of configs in the path
        if path.is_dir() {
            // Read every config files and put them into a string, if it is
            for entry in fs::read_dir(path)? {
                let entry = entry?;
                let mut config = File::open(entry.path())?;
                config.read_to_string(&mut content)?;
            }
        } else {
            // Read the config file into a string, if it isn't
            let mut config = File::open(path)?;
            config.read_to_string(&mut content)?;
        }

        // Try to parse the string, and convert it into a `Config`.
        let config = content.parse::<toml::Value>()?;
        let config = config.try_into::<Self>()?;
        Ok(config)
    }

    /// Read the config file from the default config path.
    /// If the config file is read & parsed properly, it should return
    /// a `Config`.
    pub fn read_from_default() -> Result<Self, PackageError> {
        if let Self { config_path: Some(path), .. } = Self::default() {
            Self::read_from(&path)
        } else {
            // This should never, ever, happen.
            Err(PackageError::Parsing(
                String::from("Unable to get config_path from the default cfg.")
            ))
        }
    }

    /// This function sets the None(s) in the config, to the default values.
    pub fn fill_with_default(mut self) -> Self {
        // Destructuring the default configs into individual variables.
        let Self {
            buffer_size: buf_size,
            config_path: conf_path,
            download_path: dl_path,
            unpack_path: unpk_path,
            repository: repo,
        } = Self::default();

        // If there are None(s), set them to the default value.
        set_on_none!(self, buffer_size, buf_size);
        set_on_none!(self, config_path, conf_path);
        set_on_none!(self, download_path, dl_path);
        set_on_none!(self, unpack_path, unpk_path);
        set_on_none!(self, repository, repo);

        self
    }
}
<|endoftext|>


<commit_before>//! # Config module.
//!
//! The Config module provides the beamium configuration.
//! It set defaults and then load config from '/etc', local dir and provided path.
use std::fs::File;
use std::io::Read;
use std::io;
use std::fmt;
use std::string::String;
use std::path::Path;
use std::error;
use std::error::Error;
use yaml_rust::{YamlLoader, ScanError};
use cast;
use std::collections::HashMap;
use regex;
use slog;

#[derive(Debug)]
#[derive(Clone)]
/// Config root.
pub struct Config {
    pub sources: Vec<Source>,
    pub sinks: Vec<Sink>,
    pub labels: HashMap<String, String>,
    pub parameters: Parameters,
}

#[derive(Debug)]
#[derive(Clone)]
/// Source config.
pub struct Source {
    pub name: String,
    pub url: String,
    pub period: u64,
    pub format: SourceFormat,
    pub metrics: Option<regex::RegexSet>,
}

#[derive(Debug)]
#[derive(Clone)]
/// Source format.
pub enum SourceFormat {
    Prometheus,
    Sensision,
}

#[derive(Debug)]
#[derive(Clone)]
/// Sink config.
pub struct Sink {
    pub name: String,
    pub url: String,
    pub token: String,
    pub token_header: String,
    pub selector: Option<regex::Regex>,
    pub ttl: u64,
    pub size: u64,
}

#[derive(Debug)]
#[derive(Clone)]
/// Parameters config.
pub struct Parameters {
    pub scan_period: u64,
    pub sink_dir: String,
    pub source_dir: String,
    pub batch_size: u64,
    pub batch_count: u64,
    pub log_file: String,
    pub log_level: slog::Level,
    pub timeout: u64,
}

#[derive(Debug)]
/// Config Error.
pub enum ConfigError {
    Io(io::Error),
    Yaml(ScanError),
    Regex(regex::Error),
    Format(Box<Error>),
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError {
        ConfigError::Io(err)
    }
}
impl From<ScanError> for ConfigError {
    fn from(err: ScanError) -> ConfigError {
        ConfigError::Yaml(err)
    }
}
impl From<regex::Error> for ConfigError {
    fn from(err: regex::Error) -> ConfigError {
        ConfigError::Regex(err)
    }
}
impl From<Box<Error>> for ConfigError {
    fn from(err: Box<Error>) -> ConfigError {
        ConfigError::Format(err)
    }
}
impl<'a> From<&'a str> for ConfigError {
    fn from(err: &str) -> ConfigError {
        ConfigError::Format(From::from(err))
    }
}
impl From<String> for ConfigError {
    fn from(err: String) -> ConfigError {
        ConfigError::Format(From::from(err))
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConfigError::Io(ref err) => err.fmt(f),
            ConfigError::Yaml(ref err) => err.fmt(f),
            ConfigError::Regex(ref err) => err.fmt(f),
            ConfigError::Format(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for ConfigError {
    fn description(&self) -> &str {
        match *self {
            ConfigError::Io(ref err) => err.description(),
            ConfigError::Yaml(ref err) => err.description(),
            ConfigError::Regex(ref err) => err.description(),
            ConfigError::Format(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConfigError::Io(ref err) => Some(err),
            ConfigError::Yaml(ref err) => Some(err),
            ConfigError::Regex(ref err) => Some(err),
            ConfigError::Format(ref err) => Some(err.as_ref()),
        }
    }
}

/// Load config.
///
/// Setup a defaults config and then load config from '/etc', local dir and provided path.
/// Return Err if provided path is not found or if config is unprocessable.
pub fn load_config(config_path: &str) -> Result<Config, ConfigError> {
    // Defaults
    let mut config = Config {
        sources: Vec::new(),
        labels: HashMap::new(),
        sinks: Vec::new(),
        parameters: Parameters {
            scan_period: 1000,
            sink_dir: String::from("sinks"),
            source_dir: String::from("sources"),
            batch_size: 200000,
            batch_count: 250,
            log_file: String::from(env!("CARGO_PKG_NAME")) + ".log",
            log_level: slog::Level::Info,
            timeout: 300,
        },
    };

    if config_path.is_empty() {
        // Load from etc
        if Path::new("/etc/beamium/config.yaml").exists() {
            try!(load_path("/etc/beamium/config.yaml", &mut config));
        }

        // Load local
        if Path::new("config.yaml").exists() {
            try!(load_path("config.yaml", &mut config));
        }
    } else {
        // Load from provided path
        try!(load_path(config_path, &mut config));
    }

    Ok(config)
}

/// Extend confif from file.
fn load_path<P: AsRef<Path>>(file_path: P, config: &mut Config) -> Result<(), ConfigError> {
    let mut file = try!(File::open(file_path));
    let mut contents = String::new();
    try!(file.read_to_string(&mut contents));
    let docs = try!(YamlLoader::load_from_str(&contents));

    for doc in &docs {
        if !doc["sources"].is_badvalue() {
            let sources = try!(doc["sources"]
                .as_hash()
                .ok_or("sources should be a map"));

            for (k, v) in sources {
                let name = try!(k.as_str()
                    .ok_or("sources keys should be a string"));
                let url = try!(v["url"]
                    .as_str()
                    .ok_or(format!("sources.{}.url is required and should be a string", name)));
                let period = try!(v["period"]
                    .as_i64()
                    .ok_or(format!("sources.{}.period is required and should be a number", name)));
                let period = try!(cast::u64(period)
                    .map_err(|_| format!("sources.{}.period is invalid", name)));
                let format = if v["format"].is_badvalue() {
                        SourceFormat::Prometheus
                    } else {
                        let f = try!(v["format"]
                            .as_str()
                            .ok_or(format!("sinks.{}.format should be a string", name)));

                        if f == "prometheus" {
                            SourceFormat::Prometheus
                        } else if f == "sensision" {
                            SourceFormat::Sensision
                        } else {
                            return Err(format!("sinks.{}.format should be 'Prometheus' or 'sensision'", name).into())
                        }
                    };
                let metrics = if v["metrics"].is_badvalue() {
                    None
                } else {
                    let mut metrics = Vec::new();
                    let values = try!(v["metrics"].as_vec().ok_or("metrics should be an array"));
                    for v in values {
                        let value = try!(regex::Regex::new(try!(v.as_str()
                                .ok_or(format!("metrics.{} is invalid", name)))));
                        metrics.push(String::from(r"^(\S*)\s") + value.as_str());
                    }

                    Some(try!(regex::RegexSet::new(&metrics)))
                };

                config.sources.push(Source {
                    name: String::from(name),
                    url: String::from(url),
                    period: period,
                    format: format,
                    metrics: metrics,
                })
            }
        }

        if !doc["sinks"].is_badvalue() {
            let sinks = try!(doc["sinks"].as_hash().ok_or("sinks should be a map"));
            for (k, v) in sinks {
                let name = try!(k.as_str().ok_or("sinks keys should be a string"));
                let url = try!(v["url"]
                    .as_str()
                    .ok_or(format!("sinks.{}.url is required and should be a string", name)));
                let token = try!(v["token"]
                    .as_str()
                    .ok_or(format!("sinks.{}.token is required and should be a string", name)));
                let token_header = if v["token-header"].is_badvalue() {
                    "X-Warp10-Token"
                } else {
                    try!(v["token-header"]
                        .as_str()
                        .ok_or(format!("sinks.{}.token-header should be a string", name)))
                };

                let selector = if v["selector"].is_badvalue() {
                    None
                } else {
                    Some(try!(regex::Regex::new(try!(v["selector"]
                        .as_str()
                        .ok_or(format!("sinks.{}.selector is invalid", name))))))
                };

                let ttl = if v["ttl"].is_badvalue() {
                    3600
                } else {
                    let ttl = try!(v["ttl"]
                        .as_i64()
                        .ok_or(format!("sinks.{}.ttl should be a number", name)));
                    try!(cast::u64(ttl)
                        .map_err(|_| format!("sinks.{}.ttl should be a positive number", name)))
                };

                let size = if v["size"].is_badvalue() {
                    1073741824
                } else {
                    let size = try!(v["size"]
                        .as_i64()
                        .ok_or(format!("sinks.{}.size should be a number", name)));
                    try!(cast::u64(size)
                        .map_err(|_| format!("sinks.{}.size should be a positive number", name)))
                };

                config.sinks.push(Sink {
                    name: String::from(name),
                    url: String::from(url),
                    token: String::from(token),
                    token_header: String::from(token_header),
                    selector: selector,
                    ttl: ttl,
                    size: size,
                })
            }
        }

        if !doc["labels"].is_badvalue() {
            let labels = try!(doc["labels"].as_hash().ok_or("labels should be a map"));
            for (k, v) in labels {
                let name = try!(k.as_str().ok_or("labels keys should be a string"));
                let value = try!(v.as_str()
                    .ok_or(format!("labels.{} value should be a string", name)));
                config.labels.insert(String::from(name), String::from(value));
            }
        }

        if !doc["parameters"].is_badvalue() {
            if !doc["parameters"]["source-dir"].is_badvalue() {
                let source_dir = try!(doc["parameters"]["source-dir"]
                    .as_str()
                    .ok_or(format!("parameters.source-dir should be a string")));
                config.parameters.source_dir = String::from(source_dir);
            }

            if !doc["parameters"]["sink-dir"].is_badvalue() {
                let sink_dir = try!(doc["parameters"]["sink-dir"]
                    .as_str()
                    .ok_or(format!("parameters.sink-dir should be a string")));
                config.parameters.sink_dir = String::from(sink_dir);
            }

            if !doc["parameters"]["scan-period"].is_badvalue() {
                let scan_period = try!(doc["parameters"]["scan-period"]
                    .as_i64()
                    .ok_or(format!("parameters.scan-period should be a number")));
                let scan_period = try!(cast::u64(scan_period)
                    .map_err(|_| format!("parameters.scan-period is invalid")));
                config.parameters.scan_period = scan_period;
            }

            if !doc["parameters"]["batch-size"].is_badvalue() {
                let batch_size = try!(doc["parameters"]["batch-size"]
                    .as_i64()
                    .ok_or(format!("parameters.batch-size should be a number")));
                let batch_size = try!(cast::u64(batch_size)
                    .map_err(|_| format!("parameters.batch-size is invalid")));
                config.parameters.batch_size = batch_size;
            }

            if !doc["parameters"]["batch-count"].is_badvalue() {
                let batch_count = try!(doc["parameters"]["batch-count"]
                    .as_i64()
                    .ok_or(format!("parameters.batch-count should be a number")));
                let batch_count = try!(cast::u64(batch_count)
                    .map_err(|_| format!("parameters.batch-count is invalid")));
                config.parameters.batch_count = batch_count;
            }

            if !doc["parameters"]["log-file"].is_badvalue() {
                let log_file = try!(doc["parameters"]["log-file"]
                    .as_str()
                    .ok_or(format!("parameters.log-file should be a string")));
                config.parameters.log_file = String::from(log_file);
            }

            if !doc["parameters"]["log-level"].is_badvalue() {
                let log_level = try!(doc["parameters"]["log-level"]
                    .as_i64()
                    .ok_or(format!("parameters.log-level should be a number")));
                let log_level = try!(cast::u64(log_level)
                    .map_err(|_| format!("parameters.log-level is invalid")));
                let log_level = try!(slog::Level::from_usize(log_level as usize)
                    .ok_or(format!("parameters.log-level is invalid")));
                config.parameters.log_level = log_level;
            }

            if !doc["parameters"]["timeout"].is_badvalue() {
                let timeout = try!(doc["parameters"]["timeout"]
                    .as_i64()
                    .ok_or(format!("parameters.timeout should be a number")));
                let timeout = try!(cast::u64(timeout)
                    .map_err(|_| format!("parameters.timeout is invalid")));
                config.parameters.timeout = timeout;
            }
        }
    }

    Ok(())
}
<commit_msg>feat: prepend sink selector by ^<commit_after>//! # Config module.
//!
//! The Config module provides the beamium configuration.
//! It set defaults and then load config from '/etc', local dir and provided path.
use std::fs::File;
use std::io::Read;
use std::io;
use std::fmt;
use std::string::String;
use std::path::Path;
use std::error;
use std::error::Error;
use yaml_rust::{YamlLoader, ScanError};
use cast;
use std::collections::HashMap;
use regex;
use slog;

#[derive(Debug)]
#[derive(Clone)]
/// Config root.
pub struct Config {
    pub sources: Vec<Source>,
    pub sinks: Vec<Sink>,
    pub labels: HashMap<String, String>,
    pub parameters: Parameters,
}

#[derive(Debug)]
#[derive(Clone)]
/// Source config.
pub struct Source {
    pub name: String,
    pub url: String,
    pub period: u64,
    pub format: SourceFormat,
    pub metrics: Option<regex::RegexSet>,
}

#[derive(Debug)]
#[derive(Clone)]
/// Source format.
pub enum SourceFormat {
    Prometheus,
    Sensision,
}

#[derive(Debug)]
#[derive(Clone)]
/// Sink config.
pub struct Sink {
    pub name: String,
    pub url: String,
    pub token: String,
    pub token_header: String,
    pub selector: Option<regex::Regex>,
    pub ttl: u64,
    pub size: u64,
}

#[derive(Debug)]
#[derive(Clone)]
/// Parameters config.
pub struct Parameters {
    pub scan_period: u64,
    pub sink_dir: String,
    pub source_dir: String,
    pub batch_size: u64,
    pub batch_count: u64,
    pub log_file: String,
    pub log_level: slog::Level,
    pub timeout: u64,
}

#[derive(Debug)]
/// Config Error.
pub enum ConfigError {
    Io(io::Error),
    Yaml(ScanError),
    Regex(regex::Error),
    Format(Box<Error>),
}

impl From<io::Error> for ConfigError {
    fn from(err: io::Error) -> ConfigError {
        ConfigError::Io(err)
    }
}
impl From<ScanError> for ConfigError {
    fn from(err: ScanError) -> ConfigError {
        ConfigError::Yaml(err)
    }
}
impl From<regex::Error> for ConfigError {
    fn from(err: regex::Error) -> ConfigError {
        ConfigError::Regex(err)
    }
}
impl From<Box<Error>> for ConfigError {
    fn from(err: Box<Error>) -> ConfigError {
        ConfigError::Format(err)
    }
}
impl<'a> From<&'a str> for ConfigError {
    fn from(err: &str) -> ConfigError {
        ConfigError::Format(From::from(err))
    }
}
impl From<String> for ConfigError {
    fn from(err: String) -> ConfigError {
        ConfigError::Format(From::from(err))
    }
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConfigError::Io(ref err) => err.fmt(f),
            ConfigError::Yaml(ref err) => err.fmt(f),
            ConfigError::Regex(ref err) => err.fmt(f),
            ConfigError::Format(ref err) => err.fmt(f),
        }
    }
}

impl error::Error for ConfigError {
    fn description(&self) -> &str {
        match *self {
            ConfigError::Io(ref err) => err.description(),
            ConfigError::Yaml(ref err) => err.description(),
            ConfigError::Regex(ref err) => err.description(),
            ConfigError::Format(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            ConfigError::Io(ref err) => Some(err),
            ConfigError::Yaml(ref err) => Some(err),
            ConfigError::Regex(ref err) => Some(err),
            ConfigError::Format(ref err) => Some(err.as_ref()),
        }
    }
}

/// Load config.
///
/// Setup a defaults config and then load config from '/etc', local dir and provided path.
/// Return Err if provided path is not found or if config is unprocessable.
pub fn load_config(config_path: &str) -> Result<Config, ConfigError> {
    // Defaults
    let mut config = Config {
        sources: Vec::new(),
        labels: HashMap::new(),
        sinks: Vec::new(),
        parameters: Parameters {
            scan_period: 1000,
            sink_dir: String::from("sinks"),
            source_dir: String::from("sources"),
            batch_size: 200000,
            batch_count: 250,
            log_file: String::from(env!("CARGO_PKG_NAME")) + ".log",
            log_level: slog::Level::Info,
            timeout: 300,
        },
    };

    if config_path.is_empty() {
        // Load from etc
        if Path::new("/etc/beamium/config.yaml").exists() {
            try!(load_path("/etc/beamium/config.yaml", &mut config));
        }

        // Load local
        if Path::new("config.yaml").exists() {
            try!(load_path("config.yaml", &mut config));
        }
    } else {
        // Load from provided path
        try!(load_path(config_path, &mut config));
    }

    Ok(config)
}

/// Extend confif from file.
fn load_path<P: AsRef<Path>>(file_path: P, config: &mut Config) -> Result<(), ConfigError> {
    let mut file = try!(File::open(file_path));
    let mut contents = String::new();
    try!(file.read_to_string(&mut contents));
    let docs = try!(YamlLoader::load_from_str(&contents));

    for doc in &docs {
        if !doc["sources"].is_badvalue() {
            let sources = try!(doc["sources"]
                .as_hash()
                .ok_or("sources should be a map"));

            for (k, v) in sources {
                let name = try!(k.as_str()
                    .ok_or("sources keys should be a string"));
                let url = try!(v["url"]
                    .as_str()
                    .ok_or(format!("sources.{}.url is required and should be a string", name)));
                let period = try!(v["period"]
                    .as_i64()
                    .ok_or(format!("sources.{}.period is required and should be a number", name)));
                let period = try!(cast::u64(period)
                    .map_err(|_| format!("sources.{}.period is invalid", name)));
                let format = if v["format"].is_badvalue() {
                        SourceFormat::Prometheus
                    } else {
                        let f = try!(v["format"]
                            .as_str()
                            .ok_or(format!("sinks.{}.format should be a string", name)));

                        if f == "prometheus" {
                            SourceFormat::Prometheus
                        } else if f == "sensision" {
                            SourceFormat::Sensision
                        } else {
                            return Err(format!("sinks.{}.format should be 'Prometheus' or 'sensision'", name).into())
                        }
                    };
                let metrics = if v["metrics"].is_badvalue() {
                    None
                } else {
                    let mut metrics = Vec::new();
                    let values = try!(v["metrics"].as_vec().ok_or("metrics should be an array"));
                    for v in values {
                        let value = try!(regex::Regex::new(try!(v.as_str()
                                .ok_or(format!("metrics.{} is invalid", name)))));
                        metrics.push(String::from(r"^(\S*)\s") + value.as_str());
                    }

                    Some(try!(regex::RegexSet::new(&metrics)))
                };

                config.sources.push(Source {
                    name: String::from(name),
                    url: String::from(url),
                    period: period,
                    format: format,
                    metrics: metrics,
                })
            }
        }

        if !doc["sinks"].is_badvalue() {
            let sinks = try!(doc["sinks"].as_hash().ok_or("sinks should be a map"));
            for (k, v) in sinks {
                let name = try!(k.as_str().ok_or("sinks keys should be a string"));
                let url = try!(v["url"]
                    .as_str()
                    .ok_or(format!("sinks.{}.url is required and should be a string", name)));
                let token = try!(v["token"]
                    .as_str()
                    .ok_or(format!("sinks.{}.token is required and should be a string", name)));
                let token_header = if v["token-header"].is_badvalue() {
                    "X-Warp10-Token"
                } else {
                    try!(v["token-header"]
                        .as_str()
                        .ok_or(format!("sinks.{}.token-header should be a string", name)))
                };

                let selector = if v["selector"].is_badvalue() {
                    None
                } else {
                    Some(try!(regex::Regex::new(format!("^{}", try!(v["selector"]
                        .as_str()
                        .ok_or(format!("sinks.{}.selector is invalid", name)))).as_str())))
                };

                let ttl = if v["ttl"].is_badvalue() {
                    3600
                } else {
                    let ttl = try!(v["ttl"]
                        .as_i64()
                        .ok_or(format!("sinks.{}.ttl should be a number", name)));
                    try!(cast::u64(ttl)
                        .map_err(|_| format!("sinks.{}.ttl should be a positive number", name)))
                };

                let size = if v["size"].is_badvalue() {
                    1073741824
                } else {
                    let size = try!(v["size"]
                        .as_i64()
                        .ok_or(format!("sinks.{}.size should be a number", name)));
                    try!(cast::u64(size)
                        .map_err(|_| format!("sinks.{}.size should be a positive number", name)))
                };

                config.sinks.push(Sink {
                    name: String::from(name),
                    url: String::from(url),
                    token: String::from(token),
                    token_header: String::from(token_header),
                    selector: selector,
                    ttl: ttl,
                    size: size,
                })
            }
        }

        if !doc["labels"].is_badvalue() {
            let labels = try!(doc["labels"].as_hash().ok_or("labels should be a map"));
            for (k, v) in labels {
                let name = try!(k.as_str().ok_or("labels keys should be a string"));
                let value = try!(v.as_str()
                    .ok_or(format!("labels.{} value should be a string", name)));
                config.labels.insert(String::from(name), String::from(value));
            }
        }

        if !doc["parameters"].is_badvalue() {
            if !doc["parameters"]["source-dir"].is_badvalue() {
                let source_dir = try!(doc["parameters"]["source-dir"]
                    .as_str()
                    .ok_or(format!("parameters.source-dir should be a string")));
                config.parameters.source_dir = String::from(source_dir);
            }

            if !doc["parameters"]["sink-dir"].is_badvalue() {
                let sink_dir = try!(doc["parameters"]["sink-dir"]
                    .as_str()
                    .ok_or(format!("parameters.sink-dir should be a string")));
                config.parameters.sink_dir = String::from(sink_dir);
            }

            if !doc["parameters"]["scan-period"].is_badvalue() {
                let scan_period = try!(doc["parameters"]["scan-period"]
                    .as_i64()
                    .ok_or(format!("parameters.scan-period should be a number")));
                let scan_period = try!(cast::u64(scan_period)
                    .map_err(|_| format!("parameters.scan-period is invalid")));
                config.parameters.scan_period = scan_period;
            }

            if !doc["parameters"]["batch-size"].is_badvalue() {
                let batch_size = try!(doc["parameters"]["batch-size"]
                    .as_i64()
                    .ok_or(format!("parameters.batch-size should be a number")));
                let batch_size = try!(cast::u64(batch_size)
                    .map_err(|_| format!("parameters.batch-size is invalid")));
                config.parameters.batch_size = batch_size;
            }

            if !doc["parameters"]["batch-count"].is_badvalue() {
                let batch_count = try!(doc["parameters"]["batch-count"]
                    .as_i64()
                    .ok_or(format!("parameters.batch-count should be a number")));
                let batch_count = try!(cast::u64(batch_count)
                    .map_err(|_| format!("parameters.batch-count is invalid")));
                config.parameters.batch_count = batch_count;
            }

            if !doc["parameters"]["log-file"].is_badvalue() {
                let log_file = try!(doc["parameters"]["log-file"]
                    .as_str()
                    .ok_or(format!("parameters.log-file should be a string")));
                config.parameters.log_file = String::from(log_file);
            }

            if !doc["parameters"]["log-level"].is_badvalue() {
                let log_level = try!(doc["parameters"]["log-level"]
                    .as_i64()
                    .ok_or(format!("parameters.log-level should be a number")));
                let log_level = try!(cast::u64(log_level)
                    .map_err(|_| format!("parameters.log-level is invalid")));
                let log_level = try!(slog::Level::from_usize(log_level as usize)
                    .ok_or(format!("parameters.log-level is invalid")));
                config.parameters.log_level = log_level;
            }

            if !doc["parameters"]["timeout"].is_badvalue() {
                let timeout = try!(doc["parameters"]["timeout"]
                    .as_i64()
                    .ok_or(format!("parameters.timeout should be a number")));
                let timeout = try!(cast::u64(timeout)
                    .map_err(|_| format!("parameters.timeout is invalid")));
                config.parameters.timeout = timeout;
            }
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::fmt;

pub struct Command {
    String: name,
    String: debug_msg
}

const OPCODE_DEC_B: u16 = 0x05;
const OPCODE_LD_B: u16 = 0x06;
const OPCODE_INC_C: u16 = 0x0C;

pub enum Opcode {
    DecB,
    Ldb,
    IncC
}

impl Opcode {
    pub fn parse(bits: u16) -> Opcode {
        match bits {

        }
    }
}

impl fmt::Display for Opcoe {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let code = match self {
            &Opcode::DecB => "DEC B",
            &Opcode::Ldb => "LD B, d8",
            &Opcode::IncC => "INC C",
        };

        write!(f, "{}", code);
    }
}
<commit_msg>Added the first few opcodes to the new Opcode struct<commit_after>use std::fmt;

pub struct Command {
    String: name,
    String: debug_msg
}

const OPCODE_DEC_B: u16 = 0x05;
const OPCODE_LD_B: u16 = 0x06;
const OPCODE_INC_C: u16 = 0x0C;
const OPCODE_LD_DE: u16 = 0x17;

pub enum Opcode {
    DecB,
    Ldb,
    IncC,
    Ldde,
}

impl Opcode {
    pub fn parse(bits: u16) -> Opcode {
        match bits {
            OPCODE_DEC_B => Opcode::DecB,
            OPCODE_LD_B => Opcode::Ldb,
            OPCODE_INC_C => Opcode::IncC,
            OPCODE_Ldde => Opcode::Ldde,
        }

    }
}

impl fmt::Display for Opcode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let code = match self {
            &Opcode::DecB => "DEC B",
            &Opcode::Ldb => "LD B, d8",
            &Opcode::IncC => "INC C",
        };

        write!(f, "{}", code);
    }
}
<|endoftext|>


<commit_before>use std::io;
use bytes::{BytesMut, BufMut};
use futures::{Async, Poll};

use super::IoStream;

const LW_BUFFER_SIZE: usize = 8192;
const HW_BUFFER_SIZE: usize = 32_768;

pub fn read_from_io<T: IoStream>(io: &mut T, buf: &mut BytesMut) -> Poll<usize, io::Error> {
    unsafe {
        if buf.remaining_mut() < LW_BUFFER_SIZE {
            buf.reserve(HW_BUFFER_SIZE);
        }
        match io.read(buf.bytes_mut()) {
            Ok(n) => {
                buf.advance_mut(n);
                Ok(Async::Ready(n))
            },
            Err(e) => {
                if e.kind() == io::ErrorKind::WouldBlock {
                    Ok(Async::NotReady)
                } else {
                    Err(e)
                }
            }
        }
    }
}
<commit_msg>adjust low buf size<commit_after>use std::io;
use bytes::{BytesMut, BufMut};
use futures::{Async, Poll};

use super::IoStream;

const LW_BUFFER_SIZE: usize = 4096;
const HW_BUFFER_SIZE: usize = 32_768;

pub fn read_from_io<T: IoStream>(io: &mut T, buf: &mut BytesMut) -> Poll<usize, io::Error> {
    unsafe {
        if buf.remaining_mut() < LW_BUFFER_SIZE {
            buf.reserve(HW_BUFFER_SIZE);
        }
        match io.read(buf.bytes_mut()) {
            Ok(n) => {
                buf.advance_mut(n);
                Ok(Async::Ready(n))
            },
            Err(e) => {
                if e.kind() == io::ErrorKind::WouldBlock {
                    Ok(Async::NotReady)
                } else {
                    Err(e)
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable where Self: Sized {
    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn next(&self) -> Option<Self>;

    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn prev(&self) -> Option<Self>;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: PartialOrd+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: PartialOrd+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering everything between the specified two symbols
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }

    ///
    /// Joins this range with another
    ///
    /// This creates a new range that covers all the symbols of both. If `overlaps()` is false for these two ranges, then
    /// the new range may cover additional symbols that are not in either range.
    ///
    pub fn join(&self, with: &SymbolRange<Symbol>) -> SymbolRange<Symbol> {
        SymbolRange {
            lowest:  if with.lowest<self.lowest   { with.lowest.clone()  } else { self.lowest.clone()  },
            highest: if with.highest<self.highest { self.highest.clone() } else { with.highest.clone() }
        }
    }

    ///
    /// True if this range overlaps another
    ///
    #[inline]
    pub fn overlaps(&self, with: &SymbolRange<Symbol>) -> bool {
        if self.highest < with.lowest {
            false
        } else if self.lowest > with.highest {
            false
        } else {
            true
        }
    }
}

impl Countable for u8 {
    fn next(&self) -> Option<u8> {
        if *self == Self::max_value() {
            None
        } else {
            Some(self+1)
        }
    }

    fn prev(&self) -> Option<u8> {
        if *self == Self::min_value() {
            None
        } else {
            Some(self-1)
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }

    #[test]
    fn overlaps_when_within() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(2, 3)));
    }

    #[test]
    fn overlaps_when_within_float() {
        assert!(SymbolRange::new(1.0, 4.0).overlaps(&SymbolRange::new(2.0, 3.0)));
    }

    #[test]
    fn overlaps_when_without() {
        assert!(SymbolRange::new(2, 3).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn overlaps_when_lower() {
        assert!(SymbolRange::new(1, 3).overlaps(&SymbolRange::new(2, 4)));
    }

    #[test]
    fn overlaps_when_higher() {
        assert!(SymbolRange::new(2, 4).overlaps(&SymbolRange::new(1, 3)));
    }

    #[test]
    fn overlaps_when_same() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn does_not_overlap_lower() {
        assert!(!SymbolRange::new(1, 2).overlaps(&SymbolRange::new(4, 5)));
    }

    #[test]
    fn does_not_overlap_higher() {
        assert!(!SymbolRange::new(4, 5).overlaps(&SymbolRange::new(1, 2)));
    }

    #[test]
    fn join_left() {
        let joined = SymbolRange::new(1, 3).join(&SymbolRange::new(3, 4));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_right() {
        let joined = SymbolRange::new(3, 4).join(&SymbolRange::new(1, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_overlap() {
        let joined = SymbolRange::new(1, 4).join(&SymbolRange::new(2, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }
}
<commit_msg>For our purposes, we really only need to test adjacency for countable elements (we don’t need to generate previous or next elements)<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable where Self: Sized {
    ///
    /// True if the specified value is adjacent to this one
    ///
    fn adjacent(&self, other: &Self) -> bool;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: PartialOrd+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: PartialOrd+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering everything between the specified two symbols
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }

    ///
    /// Joins this range with another
    ///
    /// This creates a new range that covers all the symbols of both. If `overlaps()` is false for these two ranges, then
    /// the new range may cover additional symbols that are not in either range.
    ///
    pub fn join(&self, with: &SymbolRange<Symbol>) -> SymbolRange<Symbol> {
        SymbolRange {
            lowest:  if with.lowest<self.lowest   { with.lowest.clone()  } else { self.lowest.clone()  },
            highest: if with.highest<self.highest { self.highest.clone() } else { with.highest.clone() }
        }
    }

    ///
    /// True if this range overlaps another
    ///
    #[inline]
    pub fn overlaps(&self, with: &SymbolRange<Symbol>) -> bool {
        if self.highest < with.lowest {
            false
        } else if self.lowest > with.highest {
            false
        } else {
            true
        }
    }
}

impl Countable for u8 {
    fn adjacent(&self, other: &Self) -> bool {
        if *self != Self::max_value() && *self+1 == *other {
            true
        } else if *self != Self::min_value() && *self-1 == *other {
            true
        } else {
            false
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }

    #[test]
    fn overlaps_when_within() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(2, 3)));
    }

    #[test]
    fn overlaps_when_within_float() {
        assert!(SymbolRange::new(1.0, 4.0).overlaps(&SymbolRange::new(2.0, 3.0)));
    }

    #[test]
    fn overlaps_when_without() {
        assert!(SymbolRange::new(2, 3).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn overlaps_when_lower() {
        assert!(SymbolRange::new(1, 3).overlaps(&SymbolRange::new(2, 4)));
    }

    #[test]
    fn overlaps_when_higher() {
        assert!(SymbolRange::new(2, 4).overlaps(&SymbolRange::new(1, 3)));
    }

    #[test]
    fn overlaps_when_same() {
        assert!(SymbolRange::new(1, 4).overlaps(&SymbolRange::new(1, 4)));
    }

    #[test]
    fn does_not_overlap_lower() {
        assert!(!SymbolRange::new(1, 2).overlaps(&SymbolRange::new(4, 5)));
    }

    #[test]
    fn does_not_overlap_higher() {
        assert!(!SymbolRange::new(4, 5).overlaps(&SymbolRange::new(1, 2)));
    }

    #[test]
    fn join_left() {
        let joined = SymbolRange::new(1, 3).join(&SymbolRange::new(3, 4));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_right() {
        let joined = SymbolRange::new(3, 4).join(&SymbolRange::new(1, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }

    #[test]
    fn join_overlap() {
        let joined = SymbolRange::new(1, 4).join(&SymbolRange::new(2, 3));

        assert!(joined.lowest == 1);
        assert!(joined.highest == 4);
    }
}
<|endoftext|>