<commit_before>use std::cmp::min;
use std::fmt;
use std::usize;

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use layout::Rect;
use style::{Color, Modifier, Style};

/// A buffer cell
#[derive(Debug, Clone, PartialEq)]
pub struct Cell {
    pub symbol: String,
    pub style: Style,
}

impl Cell {
    pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
        self.symbol.clear();
        self.symbol.push_str(symbol);
        self
    }

    pub fn set_char(&mut self, ch: char) -> &mut Cell {
        self.symbol.clear();
        self.symbol.push(ch);
        self
    }

    pub fn set_fg(&mut self, color: Color) -> &mut Cell {
        self.style.fg = color;
        self
    }

    pub fn set_bg(&mut self, color: Color) -> &mut Cell {
        self.style.bg = color;
        self
    }

    pub fn set_modifier(&mut self, modifier: Modifier) -> &mut Cell {
        self.style.modifier = modifier;
        self
    }

    pub fn set_style(&mut self, style: Style) -> &mut Cell {
        self.style = style;
        self
    }

    pub fn reset(&mut self) {
        self.symbol.clear();
        self.symbol.push(' ');
        self.style.reset();
    }
}

impl Default for Cell {
    fn default() -> Cell {
        Cell {
            symbol: " ".into(),
            style: Default::default(),
        }
    }
}

/// A buffer that maps to the desired content of the terminal after the draw call
///
/// No widget in the library interacts directly with the terminal. Instead each of them is required
/// to draw their state to an intermediate buffer. It is basically a grid where each cell contains
/// a grapheme, a foreground color and a background color. This grid will then be used to output
/// the appropriate escape sequences and characters to draw the UI as the user has defined it.
///
/// # Examples:
///
/// ```
/// # extern crate tui;
/// use tui::buffer::{Buffer, Cell};
/// use tui::layout::Rect;
/// use tui::style::{Color, Style, Modifier};
///
/// # fn main() {
/// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5});
/// buf.get_mut(0, 2).set_symbol("x");
/// assert_eq!(buf.get(0, 2).symbol, "x");
/// buf.set_string(3, 0, "string", Style::default().fg(Color::Red).bg(Color::White));
/// assert_eq!(buf.get(5, 0), &Cell{
///     symbol: String::from("r"),
///     style: Style {
///         fg: Color::Red,
///         bg: Color::White,
///         modifier: Modifier::Reset
///     }});
/// buf.get_mut(5, 0).set_char('x');
/// assert_eq!(buf.get(5, 0).symbol, "x");
/// # }
/// ```
#[derive(Clone, PartialEq)]
pub struct Buffer {
    /// The area represented by this buffer
    pub area: Rect,
    /// The content of the buffer. The length of this Vec should always be equal to area.width *
    /// area.height
    pub content: Vec<Cell>,
}

impl Default for Buffer {
    fn default() -> Buffer {
        Buffer {
            area: Default::default(),
            content: Vec::new(),
        }
    }
}

impl fmt::Debug for Buffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Buffer: {:?}", self.area)?;
        f.write_str("Content\n")?;
        for cells in self.content.chunks(self.area.width as usize) {
            for cell in cells {
                f.write_str(&cell.symbol)?;
            }
            f.write_str("\n")?;
        }
        f.write_str("Style\n")?;
        for cells in self.content.chunks(self.area.width as usize) {
            f.write_str("|")?;
            for cell in cells {
                write!(
                    f,
                    "{} {} {}|",
                    cell.style.fg.code(),
                    cell.style.bg.code(),
                    cell.style.modifier.code()
                )?;
            }
            f.write_str("\n")?;
        }
        Ok(())
    }
}

impl Buffer {
    /// Returns a Buffer with all cells set to the default one
    pub fn empty(area: Rect) -> Buffer {
        let cell: Cell = Default::default();
        Buffer::filled(area, &cell)
    }

    /// Returns a Buffer with all cells initialized with the attributes of the given Cell
    pub fn filled(area: Rect, cell: &Cell) -> Buffer {
        let size = area.area() as usize;
        let mut content = Vec::with_capacity(size);
        for _ in 0..size {
            content.push(cell.clone());
        }
        Buffer { area, content }
    }

    /// Returns a Buffer containing the given lines
    pub fn with_lines<S>(lines: Vec<S>) -> Buffer
    where
        S: AsRef<str>,
    {
        let height = lines.len() as u16;
        let width = lines.iter().fold(0, |acc, item| {
            std::cmp::max(acc, item.as_ref().width() as u16)
        });
        let mut buffer = Buffer::empty(Rect {
            x: 0,
            y: 0,
            width,
            height,
        });
        let mut y = 0;
        for line in &lines {
            buffer.set_string(0, y, line, Style::default());
            y += 1;
        }
        buffer
    }

    /// Returns the content of the buffer as a slice
    pub fn content(&self) -> &[Cell] {
        &self.content
    }

    /// Returns the area covered by this buffer
    pub fn area(&self) -> &Rect {
        &self.area
    }

    /// Returns a reference to Cell at the given coordinates
    pub fn get(&self, x: u16, y: u16) -> &Cell {
        let i = self.index_of(x, y);
        &self.content[i]
    }

    /// Returns a mutable reference to Cell at the given coordinates
    pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
        let i = self.index_of(x, y);
        &mut self.content[i]
    }

    /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates.
    ///
    /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
    ///
    /// # Examples
    ///
    /// ```
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// // Global coordinates to the top corner of this buffer's area
    /// assert_eq!(buffer.index_of(200, 100), 0);
    /// ```
    ///
    /// # Panics
    ///
    /// Panics when given an coordinate that is outside of this Buffer's area.
    ///
    /// ```should_panic
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
    /// // starts at (200, 100).
    /// buffer.index_of(0, 0); // Panics
    /// ```
    pub fn index_of(&self, x: u16, y: u16) -> usize {
        debug_assert!(
            x >= self.area.left()
                && x < self.area.right()
                && y >= self.area.top()
                && y < self.area.bottom(),
            "Trying to access position outside the buffer: x={}, y={}, area={:?}",
            x,
            y,
            self.area
        );
        ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
    }

    /// Returns the (global) coordinates of a cell given its index
    ///
    /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
    ///
    /// # Examples
    ///
    /// ```
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// assert_eq!(buffer.pos_of(0), (200, 100));
    /// assert_eq!(buffer.pos_of(14), (204, 101));
    /// ```
    ///
    /// # Panics
    ///
    /// Panics when given an index that is outside the Buffer's content.
    ///
    /// ```should_panic
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
    /// let buffer = Buffer::empty(rect);
    /// // Index 100 is the 101th cell, which lies outside of the area of this Buffer.
    /// buffer.pos_of(100); // Panics
    /// ```
    pub fn pos_of(&self, i: usize) -> (u16, u16) {
        debug_assert!(
            i < self.content.len(),
            "Trying to get the coords of a cell outside the buffer: i={} len={}",
            i,
            self.content.len()
        );
        (
            self.area.x + i as u16 % self.area.width,
            self.area.y + i as u16 / self.area.width,
        )
    }

    /// Print a string, starting at the position (x, y)
    pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
    where
        S: AsRef<str>,
    {
        self.set_stringn(x, y, string, usize::MAX, style);
    }

    /// Print at most the first n characters of a string if enough space is available
    /// until the end of the line
    pub fn set_stringn<S>(&mut self, x: u16, y: u16, string: S, limit: usize, style: Style)
    where
        S: AsRef<str>,
    {
        let mut index = self.index_of(x, y);
        let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
        let max_index = min((self.area.right() - x) as usize, limit);
        for s in graphemes.take(max_index) {
            self.content[index].symbol.clear();
            self.content[index].symbol.push_str(s);
            self.content[index].style = style;
            index += 1;
        }
    }

    /// Resize the buffer so that the mapped area matches the given area and that the buffer
    /// length is equal to area.width * area.height
    pub fn resize(&mut self, area: Rect) {
        let length = area.area() as usize;
        if self.content.len() > length {
            self.content.truncate(length);
        } else {
            self.content.resize(length, Default::default());
        }
        self.area = area;
    }

    /// Reset all cells in the buffer
    pub fn reset(&mut self) {
        for c in &mut self.content {
            c.reset();
        }
    }

    /// Merge an other buffer into this one
    pub fn merge(&mut self, other: &Buffer) {
        let area = self.area.union(other.area);
        let cell: Cell = Default::default();
        self.content.resize(area.area() as usize, cell.clone());

        // Move original content to the appropriate space
        let offset_x = self.area.x - area.x;
        let offset_y = self.area.y - area.y;
        let size = self.area.area() as usize;
        for i in (0..size).rev() {
            let (x, y) = self.pos_of(i);
            // New index in content
            let k = ((y + offset_y) * area.width + (x + offset_x)) as usize;
            self.content[k] = self.content[i].clone();
            if i != k {
                self.content[i] = cell.clone();
            }
        }

        // Push content of the other buffer into this one (may erase previous
        // data)
        let offset_x = other.area.x - area.x;
        let offset_y = other.area.y - area.y;
        let size = other.area.area() as usize;
        for i in 0..size {
            let (x, y) = other.pos_of(i);
            // New index in content
            let k = ((y + offset_y) * area.width + (x + offset_x)) as usize;
            self.content[k] = other.content[i].clone();
        }
        self.area = area;
    }
}

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

    #[test]
    fn it_translates_to_and_from_coordinates() {
        let rect = Rect::new(200, 100, 50, 80);
        let buf = Buffer::empty(rect);

        // First cell is at the upper left corner.
        assert_eq!(buf.pos_of(0), (200, 100));
        assert_eq!(buf.index_of(200, 100), 0);

        // Last cell is in the lower right.
        assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
        assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
    }

    #[test]
    #[should_panic(expected = "outside the buffer")]
    fn pos_of_panics_on_out_of_bounds() {
        let rect = Rect::new(0, 0, 10, 10);
        let buf = Buffer::empty(rect);

        // There are a total of 100 cells; zero-indexed means that 100 would be the 101st cell.
        buf.pos_of(100);
    }

    #[test]
    #[should_panic(expected = "outside the buffer")]
    fn index_of_panics_on_out_of_bounds() {
        let rect = Rect::new(0, 0, 10, 10);
        let buf = Buffer::empty(rect);

        // width is 10; zero-indexed means that 10 would be the 11th cell.
        buf.index_of(10, 0);
    }
}
<commit_msg>buffer: add quotes to fmt::Debug for better testing experience<commit_after>use std::cmp::min;
use std::fmt;
use std::usize;

use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;

use layout::Rect;
use style::{Color, Modifier, Style};

/// A buffer cell
#[derive(Debug, Clone, PartialEq)]
pub struct Cell {
    pub symbol: String,
    pub style: Style,
}

impl Cell {
    pub fn set_symbol(&mut self, symbol: &str) -> &mut Cell {
        self.symbol.clear();
        self.symbol.push_str(symbol);
        self
    }

    pub fn set_char(&mut self, ch: char) -> &mut Cell {
        self.symbol.clear();
        self.symbol.push(ch);
        self
    }

    pub fn set_fg(&mut self, color: Color) -> &mut Cell {
        self.style.fg = color;
        self
    }

    pub fn set_bg(&mut self, color: Color) -> &mut Cell {
        self.style.bg = color;
        self
    }

    pub fn set_modifier(&mut self, modifier: Modifier) -> &mut Cell {
        self.style.modifier = modifier;
        self
    }

    pub fn set_style(&mut self, style: Style) -> &mut Cell {
        self.style = style;
        self
    }

    pub fn reset(&mut self) {
        self.symbol.clear();
        self.symbol.push(' ');
        self.style.reset();
    }
}

impl Default for Cell {
    fn default() -> Cell {
        Cell {
            symbol: " ".into(),
            style: Default::default(),
        }
    }
}

/// A buffer that maps to the desired content of the terminal after the draw call
///
/// No widget in the library interacts directly with the terminal. Instead each of them is required
/// to draw their state to an intermediate buffer. It is basically a grid where each cell contains
/// a grapheme, a foreground color and a background color. This grid will then be used to output
/// the appropriate escape sequences and characters to draw the UI as the user has defined it.
///
/// # Examples:
///
/// ```
/// # extern crate tui;
/// use tui::buffer::{Buffer, Cell};
/// use tui::layout::Rect;
/// use tui::style::{Color, Style, Modifier};
///
/// # fn main() {
/// let mut buf = Buffer::empty(Rect{x: 0, y: 0, width: 10, height: 5});
/// buf.get_mut(0, 2).set_symbol("x");
/// assert_eq!(buf.get(0, 2).symbol, "x");
/// buf.set_string(3, 0, "string", Style::default().fg(Color::Red).bg(Color::White));
/// assert_eq!(buf.get(5, 0), &Cell{
///     symbol: String::from("r"),
///     style: Style {
///         fg: Color::Red,
///         bg: Color::White,
///         modifier: Modifier::Reset
///     }});
/// buf.get_mut(5, 0).set_char('x');
/// assert_eq!(buf.get(5, 0).symbol, "x");
/// # }
/// ```
#[derive(Clone, PartialEq)]
pub struct Buffer {
    /// The area represented by this buffer
    pub area: Rect,
    /// The content of the buffer. The length of this Vec should always be equal to area.width *
    /// area.height
    pub content: Vec<Cell>,
}

impl Default for Buffer {
    fn default() -> Buffer {
        Buffer {
            area: Default::default(),
            content: Vec::new(),
        }
    }
}

impl fmt::Debug for Buffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Buffer: {:?}", self.area)?;
        f.write_str("Content (quoted lines):\n")?;
        for cells in self.content.chunks(self.area.width as usize) {
            let line: String = cells.iter().map(|cell| &cell.symbol[..]).collect();
            f.write_fmt(format_args!("{:?},\n", line))?;
        }
        f.write_str("Style:\n")?;
        for cells in self.content.chunks(self.area.width as usize) {
            f.write_str("|")?;
            for cell in cells {
                write!(
                    f,
                    "{} {} {}|",
                    cell.style.fg.code(),
                    cell.style.bg.code(),
                    cell.style.modifier.code()
                )?;
            }
            f.write_str("\n")?;
        }
        Ok(())
    }
}

impl Buffer {
    /// Returns a Buffer with all cells set to the default one
    pub fn empty(area: Rect) -> Buffer {
        let cell: Cell = Default::default();
        Buffer::filled(area, &cell)
    }

    /// Returns a Buffer with all cells initialized with the attributes of the given Cell
    pub fn filled(area: Rect, cell: &Cell) -> Buffer {
        let size = area.area() as usize;
        let mut content = Vec::with_capacity(size);
        for _ in 0..size {
            content.push(cell.clone());
        }
        Buffer { area, content }
    }

    /// Returns a Buffer containing the given lines
    pub fn with_lines<S>(lines: Vec<S>) -> Buffer
    where
        S: AsRef<str>,
    {
        let height = lines.len() as u16;
        let width = lines.iter().fold(0, |acc, item| {
            std::cmp::max(acc, item.as_ref().width() as u16)
        });
        let mut buffer = Buffer::empty(Rect {
            x: 0,
            y: 0,
            width,
            height,
        });
        let mut y = 0;
        for line in &lines {
            buffer.set_string(0, y, line, Style::default());
            y += 1;
        }
        buffer
    }

    /// Returns the content of the buffer as a slice
    pub fn content(&self) -> &[Cell] {
        &self.content
    }

    /// Returns the area covered by this buffer
    pub fn area(&self) -> &Rect {
        &self.area
    }

    /// Returns a reference to Cell at the given coordinates
    pub fn get(&self, x: u16, y: u16) -> &Cell {
        let i = self.index_of(x, y);
        &self.content[i]
    }

    /// Returns a mutable reference to Cell at the given coordinates
    pub fn get_mut(&mut self, x: u16, y: u16) -> &mut Cell {
        let i = self.index_of(x, y);
        &mut self.content[i]
    }

    /// Returns the index in the Vec<Cell> for the given global (x, y) coordinates.
    ///
    /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
    ///
    /// # Examples
    ///
    /// ```
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// // Global coordinates to the top corner of this buffer's area
    /// assert_eq!(buffer.index_of(200, 100), 0);
    /// ```
    ///
    /// # Panics
    ///
    /// Panics when given an coordinate that is outside of this Buffer's area.
    ///
    /// ```should_panic
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// // Top coordinate is outside of the buffer in global coordinate space, as the Buffer's area
    /// // starts at (200, 100).
    /// buffer.index_of(0, 0); // Panics
    /// ```
    pub fn index_of(&self, x: u16, y: u16) -> usize {
        debug_assert!(
            x >= self.area.left()
                && x < self.area.right()
                && y >= self.area.top()
                && y < self.area.bottom(),
            "Trying to access position outside the buffer: x={}, y={}, area={:?}",
            x,
            y,
            self.area
        );
        ((y - self.area.y) * self.area.width + (x - self.area.x)) as usize
    }

    /// Returns the (global) coordinates of a cell given its index
    ///
    /// Global coordinates are offset by the Buffer's area offset (`x`/`y`).
    ///
    /// # Examples
    ///
    /// ```
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(200, 100, 10, 10);
    /// let buffer = Buffer::empty(rect);
    /// assert_eq!(buffer.pos_of(0), (200, 100));
    /// assert_eq!(buffer.pos_of(14), (204, 101));
    /// ```
    ///
    /// # Panics
    ///
    /// Panics when given an index that is outside the Buffer's content.
    ///
    /// ```should_panic
    /// # use tui::buffer::Buffer;
    /// # use tui::layout::Rect;
    /// let rect = Rect::new(0, 0, 10, 10); // 100 cells in total
    /// let buffer = Buffer::empty(rect);
    /// // Index 100 is the 101th cell, which lies outside of the area of this Buffer.
    /// buffer.pos_of(100); // Panics
    /// ```
    pub fn pos_of(&self, i: usize) -> (u16, u16) {
        debug_assert!(
            i < self.content.len(),
            "Trying to get the coords of a cell outside the buffer: i={} len={}",
            i,
            self.content.len()
        );
        (
            self.area.x + i as u16 % self.area.width,
            self.area.y + i as u16 / self.area.width,
        )
    }

    /// Print a string, starting at the position (x, y)
    pub fn set_string<S>(&mut self, x: u16, y: u16, string: S, style: Style)
    where
        S: AsRef<str>,
    {
        self.set_stringn(x, y, string, usize::MAX, style);
    }

    /// Print at most the first n characters of a string if enough space is available
    /// until the end of the line
    pub fn set_stringn<S>(&mut self, x: u16, y: u16, string: S, limit: usize, style: Style)
    where
        S: AsRef<str>,
    {
        let mut index = self.index_of(x, y);
        let graphemes = UnicodeSegmentation::graphemes(string.as_ref(), true);
        let max_index = min((self.area.right() - x) as usize, limit);
        for s in graphemes.take(max_index) {
            self.content[index].symbol.clear();
            self.content[index].symbol.push_str(s);
            self.content[index].style = style;
            index += 1;
        }
    }

    /// Resize the buffer so that the mapped area matches the given area and that the buffer
    /// length is equal to area.width * area.height
    pub fn resize(&mut self, area: Rect) {
        let length = area.area() as usize;
        if self.content.len() > length {
            self.content.truncate(length);
        } else {
            self.content.resize(length, Default::default());
        }
        self.area = area;
    }

    /// Reset all cells in the buffer
    pub fn reset(&mut self) {
        for c in &mut self.content {
            c.reset();
        }
    }

    /// Merge an other buffer into this one
    pub fn merge(&mut self, other: &Buffer) {
        let area = self.area.union(other.area);
        let cell: Cell = Default::default();
        self.content.resize(area.area() as usize, cell.clone());

        // Move original content to the appropriate space
        let offset_x = self.area.x - area.x;
        let offset_y = self.area.y - area.y;
        let size = self.area.area() as usize;
        for i in (0..size).rev() {
            let (x, y) = self.pos_of(i);
            // New index in content
            let k = ((y + offset_y) * area.width + (x + offset_x)) as usize;
            self.content[k] = self.content[i].clone();
            if i != k {
                self.content[i] = cell.clone();
            }
        }

        // Push content of the other buffer into this one (may erase previous
        // data)
        let offset_x = other.area.x - area.x;
        let offset_y = other.area.y - area.y;
        let size = other.area.area() as usize;
        for i in 0..size {
            let (x, y) = other.pos_of(i);
            // New index in content
            let k = ((y + offset_y) * area.width + (x + offset_x)) as usize;
            self.content[k] = other.content[i].clone();
        }
        self.area = area;
    }
}

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

    #[test]
    fn it_translates_to_and_from_coordinates() {
        let rect = Rect::new(200, 100, 50, 80);
        let buf = Buffer::empty(rect);

        // First cell is at the upper left corner.
        assert_eq!(buf.pos_of(0), (200, 100));
        assert_eq!(buf.index_of(200, 100), 0);

        // Last cell is in the lower right.
        assert_eq!(buf.pos_of(buf.content.len() - 1), (249, 179));
        assert_eq!(buf.index_of(249, 179), buf.content.len() - 1);
    }

    #[test]
    #[should_panic(expected = "outside the buffer")]
    fn pos_of_panics_on_out_of_bounds() {
        let rect = Rect::new(0, 0, 10, 10);
        let buf = Buffer::empty(rect);

        // There are a total of 100 cells; zero-indexed means that 100 would be the 101st cell.
        buf.pos_of(100);
    }

    #[test]
    #[should_panic(expected = "outside the buffer")]
    fn index_of_panics_on_out_of_bounds() {
        let rect = Rect::new(0, 0, 10, 10);
        let buf = Buffer::empty(rect);

        // width is 10; zero-indexed means that 10 would be the 11th cell.
        buf.index_of(10, 0);
    }
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::path;

use serde_yaml;

use error::*;
use files;
use frontmatter;
use legacy::wildwest;
use sass;
use site;
use syntax_highlight::has_syntax_theme;

arg_enum! {
    #[derive(Serialize, Deserialize)]
    #[derive(Debug, PartialEq, Copy, Clone)]
    pub enum Dump {
        DocObject,
        DocTemplate,
        DocLinkObject,
        Document
    }
}

impl Dump {
    pub fn is_doc(&self) -> bool {
        true
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SortOrder {
    Asc,
    Desc,
}

impl Default for SortOrder {
    fn default() -> SortOrder {
        SortOrder::Desc
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SyntaxHighlight {
    pub theme: String,
}

impl Default for SyntaxHighlight {
    fn default() -> SyntaxHighlight {
        SyntaxHighlight { theme: "base16-ocean.dark".to_owned() }
    }
}

#[derive(Debug, PartialEq, Default)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PageBuilder {
    pub default: frontmatter::FrontmatterBuilder,
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PostBuilder {
    pub name: Option<String>,
    pub description: Option<String>,
    pub dir: String,
    pub drafts_dir: Option<String>,
    pub order: SortOrder,
    pub rss: Option<String>,
    pub jsonfeed: Option<String>,
    pub default: frontmatter::FrontmatterBuilder,
}

impl Default for PostBuilder {
    fn default() -> PostBuilder {
        Self {
            name: None,
            description: None,
            dir: "posts".to_owned(),
            drafts_dir: None,
            order: SortOrder::default(),
            rss: None,
            jsonfeed: None,
            default: frontmatter::FrontmatterBuilder::new().set_post(true),
        }
    }
}

#[derive(Debug, PartialEq, Default)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AssetsBuilder {
    pub sass: sass::SassOptions,
}

const LAYOUTS_DIR: &'static str = "_layouts";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct ConfigBuilder {
    #[serde(skip)]
    pub root: path::PathBuf,
    pub source: String,
    pub destination: String,
    #[serde(skip)]
    pub abs_dest: Option<String>,
    #[serde(skip)]
    pub include_drafts: bool,
    pub default: frontmatter::FrontmatterBuilder,
    pub pages: PageBuilder,
    pub posts: PostBuilder,
    pub site: site::SiteBuilder,
    pub template_extensions: Vec<String>,
    pub ignore: Vec<String>,
    pub syntax_highlight: SyntaxHighlight,
    pub layouts_dir: &'static str,
    pub assets: AssetsBuilder,
    // This is a debug-only field and should be transient rather than persistently set.
    #[serde(skip)]
    pub dump: Vec<Dump>,
}

impl Default for ConfigBuilder {
    fn default() -> ConfigBuilder {
        ConfigBuilder {
            root: path::PathBuf::new(),
            source: "./".to_owned(),
            destination: "./".to_owned(),
            abs_dest: None,
            include_drafts: false,
            default: frontmatter::FrontmatterBuilder::new()
                .set_excerpt_separator("\n\n".to_owned())
                .set_draft(false)
                .set_post(false),
            pages: PageBuilder::default(),
            posts: PostBuilder::default(),
            site: site::SiteBuilder::default(),
            template_extensions: vec!["md".to_owned(), "liquid".to_owned()],
            ignore: vec![],
            syntax_highlight: SyntaxHighlight::default(),
            layouts_dir: LAYOUTS_DIR,
            assets: AssetsBuilder::default(),
            dump: vec![],
        }
    }
}

impl ConfigBuilder {
    pub fn from_file<P: Into<path::PathBuf>>(path: P) -> Result<ConfigBuilder> {
        Self::from_file_internal(path.into())
    }

    fn from_file_internal(path: path::PathBuf) -> Result<ConfigBuilder> {
        let content = files::read_file(&path)?;

        if content.trim().is_empty() {
            return Ok(ConfigBuilder::default());
        }

        let config: wildwest::GlobalConfig = serde_yaml::from_str(&content)?;
        let mut config: ConfigBuilder = config.into();

        let mut root = path;
        root.pop(); // Remove filename
        config.root = root;

        Ok(config)
    }

    pub fn from_cwd<P: Into<path::PathBuf>>(cwd: P) -> Result<ConfigBuilder> {
        Self::from_cwd_internal(cwd.into())
    }

    fn from_cwd_internal(cwd: path::PathBuf) -> Result<ConfigBuilder> {
        let file_path = files::find_project_file(&cwd, ".cobalt.yml");
        let mut config = file_path
            .map(|p| {
                     info!("Using config file {:?}", &p);
                     Self::from_file(&p).chain_err(|| format!("Error reading config file {:?}", p))
                 })
            .unwrap_or_else(|| {
                warn!("No .cobalt.yml file found in current directory, using default config.");
                Ok(ConfigBuilder::default())
            })?;
        config.root = cwd;
        Ok(config)
    }

    pub fn build(self) -> Result<Config> {
        let ConfigBuilder {
            root,
            source,
            destination,
            abs_dest,
            include_drafts,
            default,
            pages,
            posts,
            site,
            template_extensions,
            ignore,
            syntax_highlight,
            layouts_dir,
            assets,
            dump,
        } = self;

        let result: Result<()> = match has_syntax_theme(&syntax_highlight.theme) {
            Ok(true) => Ok(()),
            Ok(false) => {
                Err(format!("Syntax theme '{}' is unsupported", syntax_highlight.theme).into())
            }
            Err(err) => {
                warn!("Syntax theme named '{}' ignored. Reason: {}",
                      syntax_highlight.theme,
                      err);
                Ok(())
            }
        };
        result?;

        let mut pages = pages;
        pages.default = pages.default.merge(default.clone());
        let mut posts = posts;
        posts.default = posts.default.merge(default);

        if posts.dir.starts_with('/') {
            bail!("posts dir {} must be a relative path", posts.dir)
        }
        if let Some(ref drafts_dir) = posts.drafts_dir {
            if drafts_dir.starts_with('/') {
                bail!("posts dir {} must be a relative path", drafts_dir)
            }
        }

        let source = files::cleanup_path(source);
        let destination = files::cleanup_path(destination);

        let mut ignore = ignore;
        if let Ok(rel_dest) = path::Path::new(&destination).strip_prefix(&source) {
            let rel_dest = rel_dest.to_str().expect("started as a utf-8 string");
            if !rel_dest.is_empty() {
                ignore.push(format!("/{}", rel_dest.to_owned()));
            }
        }

        let source = root.join(source);
        let destination = abs_dest
            .map(|s| s.into())
            .unwrap_or_else(|| root.join(destination));

        let site = site.build(&source)?;

        let config = Config {
            source,
            destination,
            include_drafts,
            pages,
            posts,
            site,
            ignore,
            template_extensions,
            syntax_highlight,
            layouts_dir,
            assets,
            dump,
        };

        Ok(config)
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
    pub source: path::PathBuf,
    pub destination: path::PathBuf,
    pub include_drafts: bool,
    pub pages: PageBuilder,
    pub posts: PostBuilder,
    pub site: site::Site,
    pub template_extensions: Vec<String>,
    pub ignore: Vec<String>,
    pub syntax_highlight: SyntaxHighlight,
    pub layouts_dir: &'static str,
    pub assets: AssetsBuilder,
    pub dump: Vec<Dump>,
}

impl Default for Config {
    fn default() -> Config {
        ConfigBuilder::default()
            .build()
            .expect("default config should not fail")
    }
}

#[test]
fn test_from_file_ok() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: "./dest".to_owned(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_file_rss() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/rss.yml").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   posts: PostBuilder {
                       drafts_dir: Some("_drafts".to_owned()),
                       rss: Some("rss.xml".to_owned()),
                       ..Default::default()
                   },
                   site: site::SiteBuilder {
                       title: Some("My blog!".to_owned()),
                       description: Some("Blog description".to_owned()),
                       base_url: Some("http://example.com".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_file_empty() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/empty.yml").unwrap();
    assert_eq!((result), ConfigBuilder { ..Default::default() });
}

#[test]
fn test_from_file_invalid_syntax() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/invalid_syntax.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_file_not_found() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/config_does_not_exist.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_cwd_ok() {
    let result = ConfigBuilder::from_cwd("tests/fixtures/config").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: "./dest".to_owned(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_cwd_not_found() {
    let result = ConfigBuilder::from_cwd("tests/fixtures").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures").to_path_buf(),
                   ..Default::default()
               });
}

#[test]
fn test_build_default() {
    let config = ConfigBuilder::default();
    config.build().unwrap();
}
#[test]
fn test_build_dest() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    let result = result.build().unwrap();
    assert_eq!(result,
               Config {
                   source: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: path::Path::new("tests/fixtures/config/./dest").to_path_buf(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       default: frontmatter::FrontmatterBuilder::new()
                           .set_excerpt_separator("\n\n".to_owned())
                           .set_draft(false)
                           .set_post(true),
                       ..Default::default()
                   },
                   ignore: ["/dest".to_owned()].to_vec(),
                   ..Default::default()
               });
}

#[test]
fn test_build_abs_dest() {
    let mut result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    result.abs_dest = Some("hello/world".to_owned());
    let result = result.build().unwrap();
    assert_eq!(result,
               Config {
                   source: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: path::Path::new("hello/world").to_path_buf(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       default: frontmatter::FrontmatterBuilder::new()
                           .set_excerpt_separator("\n\n".to_owned())
                           .set_draft(false)
                           .set_post(true),
                       ..Default::default()
                   },
                   ignore: ["/dest".to_owned()].to_vec(),
                   ..Default::default()
               });
}

#[test]
fn test_build_posts_rel() {
    let mut config = ConfigBuilder::default();
    config.posts.dir = "rel".into();
    let config = config.build().unwrap();
    assert_eq!(config.posts.dir, "rel");
}

#[test]
fn test_build_posts_abs() {
    let mut config = ConfigBuilder::default();
    config.posts.dir = "/root".into();
    assert!(config.build().is_err());
}

#[test]
fn test_build_drafts_rel() {
    let mut config = ConfigBuilder::default();
    config.posts.drafts_dir = Some("rel".into());
    let config = config.build().unwrap();
    assert_eq!(config.posts.drafts_dir, Some("rel".into()));
}

#[test]
fn test_build_drafts_abs() {
    let mut config = ConfigBuilder::default();
    config.posts.drafts_dir = Some("/root".into());
    assert!(config.build().is_err());
}
<commit_msg>feat(config): Change future destination to _site<commit_after>use std::default::Default;
use std::path;

use serde_yaml;

use error::*;
use files;
use frontmatter;
use legacy::wildwest;
use sass;
use site;
use syntax_highlight::has_syntax_theme;

arg_enum! {
    #[derive(Serialize, Deserialize)]
    #[derive(Debug, PartialEq, Copy, Clone)]
    pub enum Dump {
        DocObject,
        DocTemplate,
        DocLinkObject,
        Document
    }
}

impl Dump {
    pub fn is_doc(&self) -> bool {
        true
    }
}

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum SortOrder {
    Asc,
    Desc,
}

impl Default for SortOrder {
    fn default() -> SortOrder {
        SortOrder::Desc
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SyntaxHighlight {
    pub theme: String,
}

impl Default for SyntaxHighlight {
    fn default() -> SyntaxHighlight {
        SyntaxHighlight { theme: "base16-ocean.dark".to_owned() }
    }
}

#[derive(Debug, PartialEq, Default)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PageBuilder {
    pub default: frontmatter::FrontmatterBuilder,
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PostBuilder {
    pub name: Option<String>,
    pub description: Option<String>,
    pub dir: String,
    pub drafts_dir: Option<String>,
    pub order: SortOrder,
    pub rss: Option<String>,
    pub jsonfeed: Option<String>,
    pub default: frontmatter::FrontmatterBuilder,
}

impl Default for PostBuilder {
    fn default() -> PostBuilder {
        Self {
            name: None,
            description: None,
            dir: "posts".to_owned(),
            drafts_dir: None,
            order: SortOrder::default(),
            rss: None,
            jsonfeed: None,
            default: frontmatter::FrontmatterBuilder::new().set_post(true),
        }
    }
}

#[derive(Debug, PartialEq, Default)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AssetsBuilder {
    pub sass: sass::SassOptions,
}

const LAYOUTS_DIR: &'static str = "_layouts";

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct ConfigBuilder {
    #[serde(skip)]
    pub root: path::PathBuf,
    pub source: String,
    pub destination: String,
    #[serde(skip)]
    pub abs_dest: Option<String>,
    #[serde(skip)]
    pub include_drafts: bool,
    pub default: frontmatter::FrontmatterBuilder,
    pub pages: PageBuilder,
    pub posts: PostBuilder,
    pub site: site::SiteBuilder,
    pub template_extensions: Vec<String>,
    pub ignore: Vec<String>,
    pub syntax_highlight: SyntaxHighlight,
    pub layouts_dir: &'static str,
    pub assets: AssetsBuilder,
    // This is a debug-only field and should be transient rather than persistently set.
    #[serde(skip)]
    pub dump: Vec<Dump>,
}

impl Default for ConfigBuilder {
    fn default() -> ConfigBuilder {
        ConfigBuilder {
            root: path::PathBuf::new(),
            source: "./".to_owned(),
            destination: "./_site".to_owned(),
            abs_dest: None,
            include_drafts: false,
            default: frontmatter::FrontmatterBuilder::new()
                .set_excerpt_separator("\n\n".to_owned())
                .set_draft(false)
                .set_post(false),
            pages: PageBuilder::default(),
            posts: PostBuilder::default(),
            site: site::SiteBuilder::default(),
            template_extensions: vec!["md".to_owned(), "liquid".to_owned()],
            ignore: vec![],
            syntax_highlight: SyntaxHighlight::default(),
            layouts_dir: LAYOUTS_DIR,
            assets: AssetsBuilder::default(),
            dump: vec![],
        }
    }
}

impl ConfigBuilder {
    pub fn from_file<P: Into<path::PathBuf>>(path: P) -> Result<ConfigBuilder> {
        Self::from_file_internal(path.into())
    }

    fn from_file_internal(path: path::PathBuf) -> Result<ConfigBuilder> {
        let content = files::read_file(&path)?;

        if content.trim().is_empty() {
            return Ok(ConfigBuilder::default());
        }

        let config: wildwest::GlobalConfig = serde_yaml::from_str(&content)?;
        let mut config: ConfigBuilder = config.into();

        let mut root = path;
        root.pop(); // Remove filename
        config.root = root;

        Ok(config)
    }

    pub fn from_cwd<P: Into<path::PathBuf>>(cwd: P) -> Result<ConfigBuilder> {
        Self::from_cwd_internal(cwd.into())
    }

    fn from_cwd_internal(cwd: path::PathBuf) -> Result<ConfigBuilder> {
        let file_path = files::find_project_file(&cwd, ".cobalt.yml");
        let mut config = file_path
            .map(|p| {
                     info!("Using config file {:?}", &p);
                     Self::from_file(&p).chain_err(|| format!("Error reading config file {:?}", p))
                 })
            .unwrap_or_else(|| {
                warn!("No .cobalt.yml file found in current directory, using default config.");
                Ok(ConfigBuilder::default())
            })?;
        config.root = cwd;
        Ok(config)
    }

    pub fn build(self) -> Result<Config> {
        let ConfigBuilder {
            root,
            source,
            destination,
            abs_dest,
            include_drafts,
            default,
            pages,
            posts,
            site,
            template_extensions,
            ignore,
            syntax_highlight,
            layouts_dir,
            assets,
            dump,
        } = self;

        let result: Result<()> = match has_syntax_theme(&syntax_highlight.theme) {
            Ok(true) => Ok(()),
            Ok(false) => {
                Err(format!("Syntax theme '{}' is unsupported", syntax_highlight.theme).into())
            }
            Err(err) => {
                warn!("Syntax theme named '{}' ignored. Reason: {}",
                      syntax_highlight.theme,
                      err);
                Ok(())
            }
        };
        result?;

        let mut pages = pages;
        pages.default = pages.default.merge(default.clone());
        let mut posts = posts;
        posts.default = posts.default.merge(default);

        if posts.dir.starts_with('/') {
            bail!("posts dir {} must be a relative path", posts.dir)
        }
        if let Some(ref drafts_dir) = posts.drafts_dir {
            if drafts_dir.starts_with('/') {
                bail!("posts dir {} must be a relative path", drafts_dir)
            }
        }

        let source = files::cleanup_path(source);
        let destination = files::cleanup_path(destination);

        let mut ignore = ignore;
        if let Ok(rel_dest) = path::Path::new(&destination).strip_prefix(&source) {
            let rel_dest = rel_dest.to_str().expect("started as a utf-8 string");
            if !rel_dest.is_empty() {
                ignore.push(format!("/{}", rel_dest.to_owned()));
            }
        }

        let source = root.join(source);
        let destination = abs_dest
            .map(|s| s.into())
            .unwrap_or_else(|| root.join(destination));

        let site = site.build(&source)?;

        let config = Config {
            source,
            destination,
            include_drafts,
            pages,
            posts,
            site,
            ignore,
            template_extensions,
            syntax_highlight,
            layouts_dir,
            assets,
            dump,
        };

        Ok(config)
    }
}

#[derive(Debug, PartialEq)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
    pub source: path::PathBuf,
    pub destination: path::PathBuf,
    pub include_drafts: bool,
    pub pages: PageBuilder,
    pub posts: PostBuilder,
    pub site: site::Site,
    pub template_extensions: Vec<String>,
    pub ignore: Vec<String>,
    pub syntax_highlight: SyntaxHighlight,
    pub layouts_dir: &'static str,
    pub assets: AssetsBuilder,
    pub dump: Vec<Dump>,
}

impl Default for Config {
    fn default() -> Config {
        ConfigBuilder::default()
            .build()
            .expect("default config should not fail")
    }
}

#[test]
fn test_from_file_ok() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: "./dest".to_owned(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_file_rss() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/rss.yml").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   posts: PostBuilder {
                       drafts_dir: Some("_drafts".to_owned()),
                       rss: Some("rss.xml".to_owned()),
                       ..Default::default()
                   },
                   site: site::SiteBuilder {
                       title: Some("My blog!".to_owned()),
                       description: Some("Blog description".to_owned()),
                       base_url: Some("http://example.com".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_file_empty() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/empty.yml").unwrap();
    assert_eq!((result), ConfigBuilder { ..Default::default() });
}

#[test]
fn test_from_file_invalid_syntax() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/invalid_syntax.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_file_not_found() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/config_does_not_exist.yml");
    assert!(result.is_err());
}

#[test]
fn test_from_cwd_ok() {
    let result = ConfigBuilder::from_cwd("tests/fixtures/config").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: "./dest".to_owned(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       ..Default::default()
                   },
                   ..Default::default()
               });
}

#[test]
fn test_from_cwd_not_found() {
    let result = ConfigBuilder::from_cwd("tests/fixtures").unwrap();
    assert_eq!(result,
               ConfigBuilder {
                   root: path::Path::new("tests/fixtures").to_path_buf(),
                   ..Default::default()
               });
}

#[test]
fn test_build_default() {
    let config = ConfigBuilder::default();
    config.build().unwrap();
}
#[test]
fn test_build_dest() {
    let result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    let result = result.build().unwrap();
    assert_eq!(result,
               Config {
                   source: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: path::Path::new("tests/fixtures/config/./dest").to_path_buf(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       default: frontmatter::FrontmatterBuilder::new()
                           .set_excerpt_separator("\n\n".to_owned())
                           .set_draft(false)
                           .set_post(true),
                       ..Default::default()
                   },
                   ignore: ["/dest".to_owned()].to_vec(),
                   ..Default::default()
               });
}

#[test]
fn test_build_abs_dest() {
    let mut result = ConfigBuilder::from_file("tests/fixtures/config/.cobalt.yml").unwrap();
    result.abs_dest = Some("hello/world".to_owned());
    let result = result.build().unwrap();
    assert_eq!(result,
               Config {
                   source: path::Path::new("tests/fixtures/config").to_path_buf(),
                   destination: path::Path::new("hello/world").to_path_buf(),
                   posts: PostBuilder {
                       dir: "_my_posts".to_owned(),
                       drafts_dir: Some("_drafts".to_owned()),
                       default: frontmatter::FrontmatterBuilder::new()
                           .set_excerpt_separator("\n\n".to_owned())
                           .set_draft(false)
                           .set_post(true),
                       ..Default::default()
                   },
                   ignore: ["/dest".to_owned()].to_vec(),
                   ..Default::default()
               });
}

#[test]
fn test_build_posts_rel() {
    let mut config = ConfigBuilder::default();
    config.posts.dir = "rel".into();
    let config = config.build().unwrap();
    assert_eq!(config.posts.dir, "rel");
}

#[test]
fn test_build_posts_abs() {
    let mut config = ConfigBuilder::default();
    config.posts.dir = "/root".into();
    assert!(config.build().is_err());
}

#[test]
fn test_build_drafts_rel() {
    let mut config = ConfigBuilder::default();
    config.posts.drafts_dir = Some("rel".into());
    let config = config.build().unwrap();
    assert_eq!(config.posts.drafts_dir, Some("rel".into()));
}

#[test]
fn test_build_drafts_abs() {
    let mut config = ConfigBuilder::default();
    config.posts.drafts_dir = Some("/root".into());
    assert!(config.build().is_err());
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#[feature(macro_rules)];
#[feature(globs)];

#[crate_type = "bin"];
#[no_uv];

extern mod capnp;
extern mod native;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnp;
    use std;

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                      message: &capnp::message::MessageBuilder) {
        capnp::serialize::write_message(writer, message);
    }

    pub fn write_buffered<T : std::io::Writer>(writer: &mut T,
                                               message: &capnp::message::MessageBuilder) {
        capnp::serialize::write_message(writer, message);
    }
/*
    pub fn new_reader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
*/
    pub fn new_buffered_reader<R: capnp::io::BufferedInputStream, T>(
        inputStream : &mut R,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnp;
    use std;
    use capnp::serialize_packed::{write_packed_message, write_packed_message_unbuffered};

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                      message: &capnp::message::MessageBuilder) {
        write_packed_message_unbuffered(writer, message);
    }

    pub fn write_buffered<T : capnp::io::BufferedOutputStream>(
        writer: &mut T,
        message: &capnp::message::MessageBuilder) {
        write_packed_message(writer, message);
    }
/*
    pub fn new_reader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : &mut capnp::io::BufferedInputStreamWrapper::new(inputStream)
            },
            options, cont)
    }
*/
    pub fn new_buffered_reader<R:capnp::io::BufferedInputStream, T>(
        inputStream : &mut R,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : inputStream
            },
            options, cont)
    }

}

static SCRATCH_SIZE : uint = 128 * 1024;
//static scratchSpace : [u8, .. 6 * SCRATCH_SIZE] = [0, .. 6 * SCRATCH_SIZE];

pub trait ScratchSpace {
    fn get_scratch<'a>(&mut self, idx : uint) -> capnp::message::FirstSegment<'a>;
}

pub struct NoScratch;

impl ScratchSpace for NoScratch {
    fn get_scratch<'a>(&mut self, _idx : uint) -> capnp::message::FirstSegment<'a> {
        capnp::message::NumWords(capnp::message::SUGGESTED_FIRST_SEGMENT_WORDS)
    }
}

pub struct UseScratch {
    scratch_space : ~[capnp::common::Word]
}

impl UseScratch {
    pub fn new() -> UseScratch {
        UseScratch {
            scratch_space : capnp::common::allocate_zeroed_words(SCRATCH_SIZE * 6)
        }
    }
}

impl ScratchSpace for UseScratch {
    fn get_scratch<'a>(&mut self, idx : uint) -> capnp::message::FirstSegment<'a> {
        assert!(idx < 6);
        unsafe {
            capnp::message::ZeroedWords(
                std::cast::transmute(
                    std::unstable::raw::Slice { data : self.scratch_space.unsafe_ref(idx * SCRATCH_SIZE),
                                                len : SCRATCH_SIZE }))
        }
    }
}

macro_rules! pass_by_object(
    ( $testcase:ident, $reuse:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    capnp::message::MessageBuilder::new($reuse.get_scratch(1),
                                                        capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                        |messageRes| {

                        let request = messageReq.init_root::<$testcase::RequestBuilder>();
                        let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                        let expected = $testcase::setup_request(&mut rng, request);

                        let requestReader = request.as_reader();
                        $testcase::handle_request(requestReader, response);

                        let responseReader = response.as_reader();
                        if (! $testcase::check_response(responseReader, expected)) {
                            fail!("Incorrect response.");
                        }
                    });
                });
            }
        });
    )

macro_rules! pass_by_bytes(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr ) => ({
            let mut requestBytes : ~[u8] = std::vec::from_elem(SCRATCH_SIZE * 8, 0u8);
            let mut responseBytes : ~[u8] = std::vec::from_elem(SCRATCH_SIZE * 8, 0u8);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    capnp::message::MessageBuilder::new($reuse.get_scratch(1),
                                                        capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                        |messageRes| {

                        let request = messageReq.init_root::<$testcase::RequestBuilder>();
                        let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                        let expected = $testcase::setup_request(&mut rng, request);

                        {
                            let mut writer = capnp::io::ArrayOutputStream::new(requestBytes);
                            $compression::write_buffered(&mut writer, messageReq)
                        }

                        $compression::new_buffered_reader(
                            &mut capnp::io::ArrayInputStream::new(requestBytes),
                            capnp::message::DEFAULT_READER_OPTIONS,
                            |requestReader| {
                                let requestReader : $testcase::RequestReader = requestReader.get_root();
                                $testcase::handle_request(requestReader, response);
                            });

                        {
                            let mut writer = capnp::io::ArrayOutputStream::new(responseBytes);
                            $compression::write_buffered(&mut writer, messageRes)
                        }

                        $compression::new_buffered_reader(
                            &mut capnp::io::ArrayInputStream::new(responseBytes),
                            capnp::message::DEFAULT_READER_OPTIONS,
                            |responseReader| {
                                let responseReader : $testcase::ResponseReader = responseReader.get_root();
                                if (! $testcase::check_response(responseReader, expected)) {
                                    fail!("Incorrect response.");
                                }
                        });
                    })
                });
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut outBuffered = capnp::io::BufferedOutputStreamWrapper::new(&mut $output);
            let mut inBuffered = capnp::io::BufferedInputStreamWrapper::new(&mut $input);
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageRes| {
                    let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                    $compression::new_buffered_reader(
                        &mut inBuffered,
                        capnp::message::DEFAULT_READER_OPTIONS,
                        |requestReader| {
                            let requestReader : $testcase::RequestReader = requestReader.get_root();
                            $testcase::handle_request(requestReader, response);
                        });
                    $compression::write_buffered(&mut outBuffered, messageRes);
                    outBuffered.flush();
                });
            }
        });
    )

macro_rules! sync_client(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            let mut outStream = std::io::stdout();
            let mut inStream = std::io::stdin();
            let mut inBuffered = capnp::io::BufferedInputStreamWrapper::new(&mut inStream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    let request = messageReq.init_root::<$testcase::RequestBuilder>();

                    let expected = $testcase::setup_request(&mut rng, request);
                    $compression::write(&mut outStream, messageReq);

                    $compression::new_buffered_reader(
                        &mut inBuffered,
                        capnp::message::DEFAULT_READER_OPTIONS,
                        |responseReader| {
                            let responseReader : $testcase::ResponseReader = responseReader.get_root();
                            assert!($testcase::check_response(responseReader, expected));
                    });
                });
            }
        });
    )

macro_rules! pass_by_pipe(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            use std::io::process;

            let mut args = std::os::args();
            args[2] = ~"client";

            let config = process::ProcessConfig {
                program: args[0].as_slice(),
                args: args.slice(1, args.len()),
                env : None,
                cwd: None,
                io : [process::CreatePipe(true, false), // stdin
                      process::CreatePipe(false, true), // stdout
                      process::Ignored]
            };
            match process::Process::new(config) {
                Some(ref mut p) => {
                    p.io.pop();
                    let mut childStdOut = p.io.pop();
                    let mut childStdIn = p.io.pop();

                    server!($testcase, $reuse, $compression, $iters, childStdOut, childStdIn);
                    println!("{}", p.wait());
                }
                None => {
                    println!("bummer");
                }
            }
        });
    )

macro_rules! do_testcase(
    ( $testcase:ident, $mode:expr, $reuse:ident, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => pass_by_object!($testcase, $reuse, $iters),
                ~"bytes" => pass_by_bytes!($testcase, $reuse, $compression, $iters),
                ~"client" => sync_client!($testcase, $reuse, $compression, $iters),
                ~"server" => {
                    let mut input = std::io::stdin();
                    let mut output = std::io::stdout();
                    server!($testcase, $reuse, $compression, $iters, input, output)
                }
                ~"pipe" => pass_by_pipe!($testcase, $reuse, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

macro_rules! do_testcase1(
    ( $testcase:expr, $mode:expr, $reuse:ident, $compression:ident, $iters:expr) => ({
            match $testcase {
                ~"carsales" => do_testcase!(carsales, $mode, $reuse, $compression, $iters),
                ~"catrank" => do_testcase!(catrank, $mode, $reuse, $compression, $iters),
                ~"eval" => do_testcase!(eval, $mode, $reuse, $compression, $iters),
                s => fail!("unrecognized test case: {}", s)
            }
        });
    )

macro_rules! do_testcase2(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $reuse {
                ~"no-reuse" => {
                    let mut scratch = NoScratch;
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                ~"reuse" => {
                    let mut scratch = UseScratch::new();
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                s => fail!("unrecognized reuse option: {}", s)
            }
        });
    )

#[start]
pub fn start (argc : int, argv: **u8) -> int {
    do native::start(argc, argv) {
        main();
    }
}

pub fn main() {
    let args = std::os::args();

    if (args.len() != 6) {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        std::os::set_exit_status(1);
        return;
    }

    let iters = match from_str::<u64>(args[5]) {
        Some (n) => n,
        None => {
            println!("Could not parse a u64 from: {}", args[5]);
            std::os::set_exit_status(1);
            return;
        }
    };

    match args[4] {
        ~"none" => do_testcase2!(args[1], args[2],  args[3], Uncompressed, iters),
        ~"packed" => do_testcase2!(args[1], args[2], args[3], Packed, iters),
        s => fail!("unrecognized compression: {}", s)
    }
}
<commit_msg>cleanup<commit_after>/*
 * Copyright (c) 2013-2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#[feature(macro_rules)];
#[feature(globs)];

#[crate_type = "bin"];
#[no_uv];

extern mod capnp;
extern mod native;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnp;
    use std;

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                      message: &capnp::message::MessageBuilder) {
        capnp::serialize::write_message(writer, message);
    }

    pub fn write_buffered<T : std::io::Writer>(writer: &mut T,
                                               message: &capnp::message::MessageBuilder) {
        capnp::serialize::write_message(writer, message);
    }
/*
    pub fn new_reader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
*/
    pub fn new_buffered_reader<R: capnp::io::BufferedInputStream, T>(
        inputStream : &mut R,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnp;
    use std;
    use capnp::serialize_packed::{write_packed_message, write_packed_message_unbuffered};

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                      message: &capnp::message::MessageBuilder) {
        write_packed_message_unbuffered(writer, message);
    }

    pub fn write_buffered<T : capnp::io::BufferedOutputStream>(
        writer: &mut T,
        message: &capnp::message::MessageBuilder) {
        write_packed_message(writer, message);
    }
/*
    pub fn new_reader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : &mut capnp::io::BufferedInputStreamWrapper::new(inputStream)
            },
            options, cont)
    }
*/
    pub fn new_buffered_reader<R:capnp::io::BufferedInputStream, T>(
        inputStream : &mut R,
        options : capnp::message::ReaderOptions,
        cont : |&mut capnp::message::MessageReader| -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : inputStream
            },
            options, cont)
    }

}

static SCRATCH_SIZE : uint = 128 * 1024;

pub trait ScratchSpace {
    fn get_scratch<'a>(&mut self, idx : uint) -> capnp::message::FirstSegment<'a>;
}

pub struct NoScratch;

impl ScratchSpace for NoScratch {
    fn get_scratch<'a>(&mut self, _idx : uint) -> capnp::message::FirstSegment<'a> {
        capnp::message::NumWords(capnp::message::SUGGESTED_FIRST_SEGMENT_WORDS)
    }
}

pub struct UseScratch {
    scratch_space : ~[capnp::common::Word]
}

impl UseScratch {
    pub fn new() -> UseScratch {
        UseScratch {
            scratch_space : capnp::common::allocate_zeroed_words(SCRATCH_SIZE * 6)
        }
    }
}

impl ScratchSpace for UseScratch {
    fn get_scratch<'a>(&mut self, idx : uint) -> capnp::message::FirstSegment<'a> {
        assert!(idx < 6);
        unsafe {
            capnp::message::ZeroedWords(
                std::cast::transmute(
                    std::unstable::raw::Slice { data : self.scratch_space.unsafe_ref(idx * SCRATCH_SIZE),
                                                len : SCRATCH_SIZE }))
        }
    }
}

macro_rules! pass_by_object(
    ( $testcase:ident, $reuse:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    capnp::message::MessageBuilder::new($reuse.get_scratch(1),
                                                        capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                        |messageRes| {

                        let request = messageReq.init_root::<$testcase::RequestBuilder>();
                        let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                        let expected = $testcase::setup_request(&mut rng, request);

                        let requestReader = request.as_reader();
                        $testcase::handle_request(requestReader, response);

                        let responseReader = response.as_reader();
                        if (! $testcase::check_response(responseReader, expected)) {
                            fail!("Incorrect response.");
                        }
                    });
                });
            }
        });
    )

macro_rules! pass_by_bytes(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr ) => ({
            let mut requestBytes : ~[u8] = std::vec::from_elem(SCRATCH_SIZE * 8, 0u8);
            let mut responseBytes : ~[u8] = std::vec::from_elem(SCRATCH_SIZE * 8, 0u8);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    capnp::message::MessageBuilder::new($reuse.get_scratch(1),
                                                        capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                        |messageRes| {

                        let request = messageReq.init_root::<$testcase::RequestBuilder>();
                        let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                        let expected = $testcase::setup_request(&mut rng, request);

                        {
                            let mut writer = capnp::io::ArrayOutputStream::new(requestBytes);
                            $compression::write_buffered(&mut writer, messageReq)
                        }

                        $compression::new_buffered_reader(
                            &mut capnp::io::ArrayInputStream::new(requestBytes),
                            capnp::message::DEFAULT_READER_OPTIONS,
                            |requestReader| {
                                let requestReader : $testcase::RequestReader = requestReader.get_root();
                                $testcase::handle_request(requestReader, response);
                            });

                        {
                            let mut writer = capnp::io::ArrayOutputStream::new(responseBytes);
                            $compression::write_buffered(&mut writer, messageRes)
                        }

                        $compression::new_buffered_reader(
                            &mut capnp::io::ArrayInputStream::new(responseBytes),
                            capnp::message::DEFAULT_READER_OPTIONS,
                            |responseReader| {
                                let responseReader : $testcase::ResponseReader = responseReader.get_root();
                                if (! $testcase::check_response(responseReader, expected)) {
                                    fail!("Incorrect response.");
                                }
                        });
                    })
                });
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut outBuffered = capnp::io::BufferedOutputStreamWrapper::new(&mut $output);
            let mut inBuffered = capnp::io::BufferedInputStreamWrapper::new(&mut $input);
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageRes| {
                    let response = messageRes.init_root::<$testcase::ResponseBuilder>();
                    $compression::new_buffered_reader(
                        &mut inBuffered,
                        capnp::message::DEFAULT_READER_OPTIONS,
                        |requestReader| {
                            let requestReader : $testcase::RequestReader = requestReader.get_root();
                            $testcase::handle_request(requestReader, response);
                        });
                    $compression::write_buffered(&mut outBuffered, messageRes);
                    outBuffered.flush();
                });
            }
        });
    )

macro_rules! sync_client(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            let mut outStream = std::io::stdout();
            let mut inStream = std::io::stdin();
            let mut inBuffered = capnp::io::BufferedInputStreamWrapper::new(&mut inStream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                capnp::message::MessageBuilder::new($reuse.get_scratch(0),
                                                    capnp::message::SUGGESTED_ALLOCATION_STRATEGY,
                                                    |messageReq| {
                    let request = messageReq.init_root::<$testcase::RequestBuilder>();

                    let expected = $testcase::setup_request(&mut rng, request);
                    $compression::write(&mut outStream, messageReq);

                    $compression::new_buffered_reader(
                        &mut inBuffered,
                        capnp::message::DEFAULT_READER_OPTIONS,
                        |responseReader| {
                            let responseReader : $testcase::ResponseReader = responseReader.get_root();
                            assert!($testcase::check_response(responseReader, expected));
                    });
                });
            }
        });
    )

macro_rules! pass_by_pipe(
    ( $testcase:ident, $reuse:ident, $compression:ident, $iters:expr) => ({
            use std::io::process;

            let mut args = std::os::args();
            args[2] = ~"client";

            let config = process::ProcessConfig {
                program: args[0].as_slice(),
                args: args.slice(1, args.len()),
                env : None,
                cwd: None,
                io : [process::CreatePipe(true, false), // stdin
                      process::CreatePipe(false, true), // stdout
                      process::Ignored]
            };
            match process::Process::new(config) {
                Some(ref mut p) => {
                    p.io.pop();
                    let mut childStdOut = p.io.pop();
                    let mut childStdIn = p.io.pop();

                    server!($testcase, $reuse, $compression, $iters, childStdOut, childStdIn);
                    println!("{}", p.wait());
                }
                None => {
                    println!("bummer");
                }
            }
        });
    )

macro_rules! do_testcase(
    ( $testcase:ident, $mode:expr, $reuse:ident, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => pass_by_object!($testcase, $reuse, $iters),
                ~"bytes" => pass_by_bytes!($testcase, $reuse, $compression, $iters),
                ~"client" => sync_client!($testcase, $reuse, $compression, $iters),
                ~"server" => {
                    let mut input = std::io::stdin();
                    let mut output = std::io::stdout();
                    server!($testcase, $reuse, $compression, $iters, input, output)
                }
                ~"pipe" => pass_by_pipe!($testcase, $reuse, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

macro_rules! do_testcase1(
    ( $testcase:expr, $mode:expr, $reuse:ident, $compression:ident, $iters:expr) => ({
            match $testcase {
                ~"carsales" => do_testcase!(carsales, $mode, $reuse, $compression, $iters),
                ~"catrank" => do_testcase!(catrank, $mode, $reuse, $compression, $iters),
                ~"eval" => do_testcase!(eval, $mode, $reuse, $compression, $iters),
                s => fail!("unrecognized test case: {}", s)
            }
        });
    )

macro_rules! do_testcase2(
    ( $testcase:expr, $mode:expr, $reuse:expr, $compression:ident, $iters:expr) => ({
            match $reuse {
                ~"no-reuse" => {
                    let mut scratch = NoScratch;
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                ~"reuse" => {
                    let mut scratch = UseScratch::new();
                    do_testcase1!($testcase, $mode, scratch, $compression, $iters)
                }
                s => fail!("unrecognized reuse option: {}", s)
            }
        });
    )

#[start]
pub fn start (argc : int, argv: **u8) -> int {
    do native::start(argc, argv) {
        main();
    }
}

pub fn main() {
    let args = std::os::args();

    if (args.len() != 6) {
        println!("USAGE: {} CASE MODE REUSE COMPRESSION ITERATION_COUNT", args[0]);
        std::os::set_exit_status(1);
        return;
    }

    let iters = match from_str::<u64>(args[5]) {
        Some (n) => n,
        None => {
            println!("Could not parse a u64 from: {}", args[5]);
            std::os::set_exit_status(1);
            return;
        }
    };

    match args[4] {
        ~"none" => do_testcase2!(args[1], args[2],  args[3], Uncompressed, iters),
        ~"packed" => do_testcase2!(args[1], args[2], args[3], Packed, iters),
        s => fail!("unrecognized compression: {}", s)
    }
}
<|endoftext|>


<commit_before>//! Definition of various types related to connexions

/// Type of the ether_type
pub type EtherType = u16;
/// Type that represent a protocol id
pub type ProtocolIdType = u8;
/// Type that represent a port
pub type PortType = u16;
<commit_msg>net/conn: Add type EthernetRule, NetworkRule and TransportRule<commit_after>//! Definition of various types related to connexions

use net::defs::{HwAddr, IpAddr};

/// Type of the ether_type
pub type EtherType = u16;
/// Type that represent a protocol id
pub type ProtocolIdType = u8;
/// Type that represent a port
pub type PortType = u16;

/// Ethernet layer part of the rule
pub struct EthernetRule {
    pub ether_type: EtherType,
    pub hw_src: Option<HwAddr>,
}

/// Network layer part of the rule
pub struct NetworkRule {
    pub protocol_id: ProtocolIdType,
    pub ip_src: Option<IpAddr>,
}

/// Transport layer part of the rule
pub struct TransportRule {
    pub port: PortType,
}
<|endoftext|>


<commit_before>use player::Player;
use patterns::Patterns;
use construct_field::construct_field;

fn construct_patterns(strings: &[&str]) -> Patterns {
  let mut pattern_s = String::new();
  let mut p = Patterns::empty();
  for s in strings {
    pattern_s.clear();
    for line in s.split('\n').map(|line| line.trim_matches(' ')).filter(|line| !line.is_empty()) {
      pattern_s.push_str(line);
      pattern_s.push('\n');
    }
    p.add_str(pattern_s.as_str());
  }
  p
}

#[test]
#[should_panic]
fn pattern_moves_discrepancy() {
  construct_patterns(&[
    "
    4 4 1.0
    ....
    .RB.
    .BR.
    ..+.
    3 3 1.0
    "
  ]);
}

#[test]
#[should_panic]
fn pattern_without_moves_on_image() {
  construct_patterns(&[
    "
    4 4 1.0
    ....
    .RB.
    .BR.
    ....
    3 3 1.0
    "
  ]);
}

#[test]
fn pattern_empty_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #B..
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_borders_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #BR.
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(2, 3), 1f64)]);
}

#[test]
fn pattern_borders_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #BR.
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ....
    .aA.
    .Aa.
    ....
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_any_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    ****
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(2, 3), 1f64)]);
}

#[test]
fn pattern_any_except_border_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #???
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(2, 3), 1f64)]);
}

#[test]
fn pattern_any_except_border_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    ????
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_red_black_or_none_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #Rbb
    #Brr
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(2, 3), 1f64)]);
}

#[test]
fn pattern_red_black_or_none_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #bbb
    #rrr
    #..+
    3 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}
<commit_msg>Add patterns tests.<commit_after>use player::Player;
use patterns::Patterns;
use construct_field::construct_field;

fn construct_patterns(strings: &[&str]) -> Patterns {
  let mut pattern_s = String::new();
  let mut p = Patterns::empty();
  for s in strings {
    pattern_s.clear();
    for line in s.split('\n').map(|line| line.trim_matches(' ')).filter(|line| !line.is_empty()) {
      pattern_s.push_str(line);
      pattern_s.push('\n');
    }
    p.add_str(pattern_s.as_str());
  }
  p
}

#[test]
#[should_panic]
fn pattern_moves_discrepancy() {
  construct_patterns(&[
    "
    4 4 1.0
    ....
    .RB.
    .BR.
    .+..
    2 3 1.0
    "
  ]);
}

#[test]
#[should_panic]
fn pattern_without_moves_on_image() {
  construct_patterns(&[
    "
    4 4 1.0
    ....
    .RB.
    .BR.
    ....
    2 3 1.0
    "
  ]);
}

#[test]
fn pattern_empty_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #B..
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_borders_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #BR.
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 3), 1f64)]);
}

#[test]
fn pattern_borders_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #BR.
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ....
    .aA.
    .Aa.
    ....
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_any_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    ****
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 3), 1f64)]);
}

#[test]
fn pattern_any_except_border_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    #???
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 3), 1f64)]);
}

#[test]
fn pattern_any_except_border_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #RB.
    ????
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_red_black_or_none_matches() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #Rbb
    #Brr
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 3), 1f64)]);
}

#[test]
fn pattern_red_black_or_none_doesnt_match() {
  let p = construct_patterns(&[
    "
    4 4 1.0
    #...
    #bbb
    #rrr
    #.+.
    2 3 1.0
    "
  ]);
  let field = construct_field(
    "
    ...
    aA.
    Aa.
    ...
    "
  );
  assert!(p.find(&field, Player::Red, false).is_empty());
}

#[test]
fn pattern_rotation_0() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .aA..
    .Aa..
    .A.a.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(0, 1), 1f64)]);
}

#[test]
fn pattern_rotation_1() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    ...a.
    .Aa..
    .aAA.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 4), 1f64)]);
}

#[test]
fn pattern_rotation_2() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .a.A.
    ..aA.
    ..Aa.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(4, 3), 1f64)]);
}

#[test]
fn pattern_rotation_3() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .AAa.
    ..aA.
    .a...
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(3, 0), 1f64)]);
}

#[test]
fn pattern_rotation_4() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    ..Aa.
    ..aA.
    .a.A.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(4, 1), 1f64)]);
}

#[test]
fn pattern_rotation_5() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .aAA.
    .Aa..
    ...a.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(1, 0), 1f64)]);
}

#[test]
fn pattern_rotation_6() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .A.a.
    .Aa..
    .aA..
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(0, 3), 1f64)]);
}

#[test]
fn pattern_rotation_7() {
  let p = construct_patterns(&[
    "
    5 5 1.0
    .....
    +RB..
    .BR..
    .B.R.
    .....
    0 1 1.0
    "
  ]);
  let field = construct_field(
    "
    .....
    .a...
    ..aA.
    .AAa.
    .....
    "
  );
  assert_eq!(p.find(&field, Player::Red, false), vec![(field.to_pos(3, 4), 1f64)]);
}
<|endoftext|>