<commit_before>fn is_vowel(c: char) -> bool {
    match c {
        'a' => true,
        'e' => true,
        'i' => true,
        'o' => true,
        'u' => true,
        _ => false,
    }
}

fn has_good_pair(string: &str) -> bool {
    let chars = string.as_bytes();
    for i in 0..chars.len() - 1 {
        if chars[i] == chars[i + 1] {
            return true;
        }
    }
    false
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn has_bad_pair(string: &str) -> bool {
    string.contains("ab") ||
        string.contains("cd") ||
        string.contains("pq") ||
        string.contains("xy")
}

#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn is_nice(string: &str) -> bool {
    string.chars().filter(|c| is_vowel(*c)).count() >= 3 &&
        has_good_pair(string) &&
        !has_bad_pair(string)
}

pub fn process_file(path: &str) {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::BufReader;

    let file = File::open(path).unwrap();
    let file = BufReader::new(file);
    let mut nice_count = 0;
    for line in file.lines() {
        if is_nice(&line.unwrap()) {
            nice_count += 1;
        }
    }
    println!("Part 1: {}", nice_count);
}

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

    #[test]
    fn test_is_nice() {
        assert!(is_nice("ugknbfddgicrmopn"));
        assert!(is_nice("aaa"));
        assert!(!is_nice("jchzalrnumimnmhp"));
        assert!(!is_nice("haegwjzuvuyypxyu"));
        assert!(!is_nice("dvszwmarrgswjxmb"));
    }
}
<commit_msg>Add day 5<commit_after>use std::collections::HashMap;

fn is_vowel(c: char) -> bool {
    match c {
        'a' => true,
        'e' => true,
        'i' => true,
        'o' => true,
        'u' => true,
        _ => false,
    }
}

fn has_good_pair(string: &str) -> bool {
    let chars = string.as_bytes();
    for i in 0..chars.len() - 1 {
        if chars[i] == chars[i + 1] {
            return true;
        }
    }
    false
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn has_bad_pair(string: &str) -> bool {
    string.contains("ab") ||
        string.contains("cd") ||
        string.contains("pq") ||
        string.contains("xy")
}

#[cfg_attr(rustfmt, rustfmt_skip)]
pub fn is_nice(string: &str) -> bool {
    string.chars().filter(|c| is_vowel(*c)).count() >= 3 &&
        has_good_pair(string) &&
        !has_bad_pair(string)
}

fn has_good_trigram(string: &str) -> bool {
    let chars = string.as_bytes();
    for i in 0..chars.len() - 2 {
        if chars[i] == chars[i + 2] {
            return true;
        }
    }
    false
}

fn bigrams(string: &str) -> Vec<(char, char)> {
    string.chars().zip(string.chars().skip(1)).collect()
}

fn has_two_good_pairs(string: &str) -> bool {
    let bs = bigrams(string);
    let mut seen = HashMap::new();
    for i in 0..bs.len() {
        let loc = seen.entry(bs[i]).or_insert(i);
        if *loc + 2 <= i {
            return true;
        }
    }
    false
}

pub fn is_nice_part2(string: &str) -> bool {
    has_good_trigram(string) && has_two_good_pairs(string)
}

pub fn process_file(path: &str) {
    use std::fs::File;
    use std::io::prelude::*;
    use std::io::BufReader;

    let file = File::open(path).unwrap();
    let file = BufReader::new(file);
    let (mut part1, mut part2) = (0, 0);
    for line in file.lines() {
        let line = &line.unwrap();
        if is_nice(line) {
            part1 += 1;
        }
        if is_nice_part2(line) {
            part2 += 1;
        }
    }
    println!("Part 1: {}", part1);
    println!("Part 2: {}", part2);
}

#[cfg(test)]
mod tests {
    use super::{is_nice, is_nice_part2};

    #[test]
    fn test_is_nice() {
        assert!(is_nice("ugknbfddgicrmopn"));
        assert!(is_nice("aaa"));
        assert!(!is_nice("jchzalrnumimnmhp"));
        assert!(!is_nice("haegwjzuvuyypxyu"));
        assert!(!is_nice("dvszwmarrgswjxmb"));
    }

    #[test]
    fn test_is_nice_part2() {
        assert!(is_nice_part2("qjhvhtzxzqqjkmpb"));
        assert!(is_nice_part2("xxyxx"));
        assert!(!is_nice_part2("uurcxstgmygtbstg"));
        assert!(!is_nice_part2("ieodomkazucvgmuy"));
    }
}
<|endoftext|>


<commit_before>use luminance::{Dim2, Flat, RGBA32F, Texture};

/// Time.
pub type Time = f32;

/// A clip is an object that implements a visual sequence.
pub struct Clip<'a> {
  act: Box<Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> + 'a>
}

impl<'a> Clip<'a> {
  pub fn new<F>(act: F) -> Self where F: 'a + Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    Clip {
      act: Box::new(act)
    }
  }
}

/// A cut is an object that slices a `Clip` at an *input time* and *output time*. It is instantiated
/// in a `Track` at a given *instance time*.
#[derive(Clone)]
pub struct Cut<'a> {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: &'a Clip<'a>
}

impl<'a> Cut<'a> {
  pub fn new(in_time: Time, out_time: Time, inst_time: Time, clip: &'a Clip<'a>) -> Self {
    Cut {
      in_time: in_time,
      out_time: out_time,
      inst_time: inst_time,
      clip: clip
    }
  }
}

/// A track gathers `Cut`s and its purpose is to be used inside a `Timeline`.
#[derive(Clone)]
pub struct Track<'a> {
  cuts: Vec<Cut<'a>>
}

impl<'a> Track<'a> {
  pub fn new() -> Self {
    Track {
      cuts: Vec::new()
    }
  }

  pub fn add_cut(&mut self, cut: Cut<'a>) {
    self.cuts.push(cut);
  }
}

impl<'a, 'b> From<&'b [Cut<'a>]> for Track<'a> {
  fn from(cuts: &'b [Cut<'a>]) -> Self {
    Track {
      cuts: cuts.to_vec()
    }
  }
}

/// A timeline gathers tracks used to build up the visual aspect of the demo.
#[derive(Clone)]
pub struct Timeline<'a> {
  tracks: Vec<Track<'a>>,
  cross_list: CrossList<'a>
}

impl<'a> Timeline<'a> {
  pub fn new() -> Self {
    Timeline {
      tracks: Vec::new(),
      cross_list: CrossList::new()
    }
  }

  pub fn add_track(&mut self, track: Track<'a>) {
    self.tracks.push(track);
  }
}

impl<'a, 'b> From<&'b [Track<'a>]> for Timeline<'a> {
  fn from(tracks: &'b [Track<'a>]) -> Self {
    Timeline {
      tracks: tracks.to_vec(),
      cross_list: CrossList::new()
    }
  }
}

/// An optimized structure used to put spanning regions over cuts of several tracks in a timeline in
/// order to know which cuts of which tracks are active at any time in O(1). A track cross
/// represents such a single region, and a list of ascending-ordered track cross can be used to
/// render the demo in an optimized way.
#[derive(Clone)]
struct TrackCross<'a> {
  /// Time at which the track cross begins.
  in_time: Time,
  /// Duration.
  out_time: Time,
  /// List of cuts that are active at the given time.
  cuts: Vec<&'a Cut<'a>>
}

impl<'a> TrackCross<'a> {
  pub fn new(in_time: Time, out_time: Time, cuts: &'a [&'a Cut<'a>]) -> Self {
    TrackCross {
      in_time: in_time,
      out_time: out_time,
      cuts: cuts.to_vec()
    }
  }

  // TODO
  /// Perform a cross track cut act.
  pub fn cross_act(&self, t: Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    if self.cuts.len() != 1 {
      unimplemented!();
    }

    // convert t into the clip’s time space
    let cut = self.cuts[0];
    let clip_t = t - cut.inst_time;

    (cut.clip.act)(clip_t)
  }
}

/// A list of cut cross optimized for forward playing.
#[derive(Clone)]
struct CrossList<'a> {
  track_cross: Vec<TrackCross<'a>>,
  current: usize
}

impl<'a> CrossList<'a> {
  fn new() -> Self {
    CrossList {
      track_cross: Vec::new(),
      current: 0
    }
  }
}
<commit_msg>Cross stuff.<commit_after>use luminance::{Dim2, Flat, RGBA32F, Texture};

/// Time.
pub type Time = f32;

/// A clip is an object that implements a visual sequence.
pub struct Clip<'a> {
  act: Box<Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> + 'a>
}

impl<'a> Clip<'a> {
  pub fn new<F>(act: F) -> Self where F: 'a + Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    Clip {
      act: Box::new(act)
    }
  }
}

/// A cut is an object that slices a `Clip` at an *input time* and *output time*. It is instantiated
/// in a `Track` at a given *instance time*.
#[derive(Clone)]
pub struct Cut<'a> {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: &'a Clip<'a>
}

impl<'a> Cut<'a> {
  pub fn new(in_time: Time, out_time: Time, inst_time: Time, clip: &'a Clip<'a>) -> Self {
    Cut {
      in_time: in_time,
      out_time: out_time,
      inst_time: inst_time,
      clip: clip
    }
  }
}

/// A track gathers `Cut`s and its purpose is to be used inside a `Timeline`.
#[derive(Clone)]
pub struct Track<'a> {
  cuts: Vec<Cut<'a>>
}

impl<'a> Track<'a> {
  pub fn new() -> Self {
    Track {
      cuts: Vec::new()
    }
  }

  pub fn add_cut(&mut self, cut: Cut<'a>) {
    self.cuts.push(cut);
  }
}

impl<'a, 'b> From<&'b [Cut<'a>]> for Track<'a> {
  fn from(cuts: &'b [Cut<'a>]) -> Self {
    Track {
      cuts: cuts.to_vec()
    }
  }
}

/// A timeline gathers tracks used to build up the visual aspect of the demo.
#[derive(Clone)]
pub struct Timeline<'a> {
  tracks: Vec<Track<'a>>,
}

impl<'a> Timeline<'a> {
  pub fn new() -> Self {
    Timeline {
      tracks: Vec::new(),
    }
  }

  pub fn add_track(&mut self, track: Track<'a>) {
    self.tracks.push(track);
  }

  fn cross_list(&self) -> CrossList<'a> {
    unimplemented!();
  }
}

impl<'a, 'b> From<&'b [Track<'a>]> for Timeline<'a> {
  fn from(tracks: &'b [Track<'a>]) -> Self {
    Timeline {
      tracks: tracks.to_vec(),
    }
  }
}

/// An optimized structure used to put spanning regions over cuts of several tracks in a timeline in
/// order to know which cuts of which tracks are active at any time in O(1). A track cross
/// represents such a single region, and a list of ascending-ordered track cross can be used to
/// render the demo in an optimized way.
#[derive(Clone)]
struct TrackCross<'a> {
  /// Time at which the track cross begins.
  in_time: Time,
  /// Duration.
  out_time: Time,
  /// List of cuts that are active at the given time.
  cuts: Vec<&'a Cut<'a>>
}

impl<'a> TrackCross<'a> {
  pub fn new(in_time: Time, out_time: Time, cuts: &'a [&'a Cut<'a>]) -> Self {
    TrackCross {
      in_time: in_time,
      out_time: out_time,
      cuts: cuts.to_vec()
    }
  }

  // TODO
  /// Perform a cross track cut act.
  pub fn cross_act(&self, t: Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    if self.cuts.len() != 1 {
      unimplemented!();
    }

    // convert t into the clip’s time space
    let cut = self.cuts[0];
    let clip_t = t - cut.inst_time;

    (cut.clip.act)(clip_t)
  }
}

/// A list of track crosses optimized for forward playing.
#[derive(Clone)]
struct CrossList<'a> {
  track_cross: Vec<TrackCross<'a>>,
  current: usize
}

impl<'a> CrossList<'a> {
  fn new() -> Self {
    CrossList {
      track_cross: Vec::new(),
      current: 0
    }
  }
}
<|endoftext|>


<commit_before>use r2d2;
use r2d2_postgres::PostgresConnectionManager;

use super::errors::*;
use super::board::{Board, NewBoard};
use super::post::Post;

pub type Pool = r2d2::Pool<PostgresConnectionManager>;

pub fn create_tables(pool: Pool) -> Result<()> {
    let conn = pool.get().unwrap();
    // Create the boards table
    conn.execute("CREATE TABLE IF NOT EXISTS boards (
                    short_name VARCHAR PRIMARY KEY,
                    long_name VARCHAR NOT NULL,
                    description TEXT NOT NULL,
                    post_number BIGINT NOT NULL,
                    active_threads BIGINT[] NOT NULL
                  )",
                 &[])?;

    // Create the posts table
    conn.execute("CREATE TABLE IF NOT EXISTS posts (
                    post_number BIGINT NOT NULL,
                    board VARCHAR NOT NULL,
                    subject VARCHAR,
                    name VARCHAR NOT NULL,
                    email VARCHAR NOT NULL,
                    content TEXT NOT NULL,
                    thread boolean NOT NULL,
                    parent BIGINT,
                    PRIMARY KEY (board, post_number)
                  )",
                 &[])?;

    Ok(())
}

pub fn create_boards(pool: Pool, boards: &[NewBoard]) -> Result<()> {
    let conn = pool.get().unwrap();

    for b in boards {
        conn.execute("INSERT INTO boards (short_name, long_name, description, post_number, \
                      active_threads) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (short_name) DO \
                      NOTHING",
                     &[&b.short_name, &b.long_name, &b.description, &0i64, &Vec::<i64>::new()])?;
    }

    Ok(())
}

pub fn get_board(pool: Pool, board_name: &str) -> Result<Option<Board>> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("SELECT short_name, long_name, description, post_number, active_threads \
                    FROM boards WHERE short_name = $1",
                   &[&board_name])?;
    if rows.is_empty() {
        Ok(None)
    } else {
        let row = rows.get(0);
        let board = Board {
            short_name: row.get(0),
            long_name: row.get(1),
            description: row.get(2),
            post_number: row.get(3),
            active_threads: row.get(4),
        };
        Ok(Some(board))
    }
}

pub fn get_post_number(pool: Pool, board_name: &str) -> Result<i64> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("UPDATE boards SET post_number = post_number + 1 WHERE short_name = $1 \
                    RETURNING post_number",
                   &[&board_name])?;
    Ok(rows.get(0).get(0))
}

pub fn board_exists(pool: Pool, board_name: &str) -> Result<bool> {
    let conn = pool.get().unwrap();

    let rows = conn.query("SELECT short_name FROM boards WHERE short_name = $1",
               &[&board_name])?;
    Ok(!rows.is_empty())
}

pub fn create_thread(pool: Pool, thread: Post) -> Result<()> {
    let conn = pool.get().unwrap();

    info!("Inserting new thread into posts table");
    conn.execute("INSERT INTO posts (post_number, board, subject, name, email, content, thread, \
                  parent) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
                 &[&thread.post_number,
                   &thread.board,
                   &thread.subject,
                   &thread.name,
                   &thread.email,
                   &thread.content,
                   &thread.thread,
                   &thread.parent])?;

    info!("Adding new thread to board list");
    conn.execute("UPDATE boards SET active_threads = $1::BIGINT || active_threads WHERE \
                  short_name = $2",
                 &[&thread.post_number, &thread.board])?;
    Ok(())
}

pub fn get_thread(pool: Pool, board_name: &str, thread_number: i64) -> Result<Option<Post>> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("SELECT post_number, board, subject, name, email, content, thread, parent \
                    FROM posts WHERE board = $1 AND post_number = $2",
                   &[&board_name, &thread_number])?;

    if rows.is_empty() {
        Ok(None)
    } else {
        let row = rows.get(0);
        let thread = Post {
            post_number: row.get(0),
            board: row.get(1),
            subject: row.get(2),
            name: row.get(3),
            email: row.get(4),
            content: row.get(5),
            thread: row.get(6),
            parent: row.get(7),
        };
        Ok(Some(thread))
    }
}
<commit_msg>Make thread creation a single transaction<commit_after>use r2d2;
use r2d2_postgres::PostgresConnectionManager;

use super::errors::*;
use super::board::{Board, NewBoard};
use super::post::Post;

pub type Pool = r2d2::Pool<PostgresConnectionManager>;

pub fn create_tables(pool: Pool) -> Result<()> {
    let conn = pool.get().unwrap();
    // Create the boards table
    conn.execute("CREATE TABLE IF NOT EXISTS boards (
                    short_name VARCHAR PRIMARY KEY,
                    long_name VARCHAR NOT NULL,
                    description TEXT NOT NULL,
                    post_number BIGINT NOT NULL,
                    active_threads BIGINT[] NOT NULL
                  )",
                 &[])?;

    // Create the posts table
    conn.execute("CREATE TABLE IF NOT EXISTS posts (
                    post_number BIGINT NOT NULL,
                    board VARCHAR NOT NULL,
                    subject VARCHAR,
                    name VARCHAR NOT NULL,
                    email VARCHAR NOT NULL,
                    content TEXT NOT NULL,
                    thread boolean NOT NULL,
                    parent BIGINT,
                    PRIMARY KEY (board, post_number)
                  )",
                 &[])?;

    Ok(())
}

pub fn create_boards(pool: Pool, boards: &[NewBoard]) -> Result<()> {
    let conn = pool.get().unwrap();

    for b in boards {
        conn.execute("INSERT INTO boards (short_name, long_name, description, post_number, \
                      active_threads) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (short_name) DO \
                      NOTHING",
                     &[&b.short_name, &b.long_name, &b.description, &0i64, &Vec::<i64>::new()])?;
    }

    Ok(())
}

pub fn get_board(pool: Pool, board_name: &str) -> Result<Option<Board>> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("SELECT short_name, long_name, description, post_number, active_threads \
                    FROM boards WHERE short_name = $1",
                   &[&board_name])?;
    if rows.is_empty() {
        Ok(None)
    } else {
        let row = rows.get(0);
        let board = Board {
            short_name: row.get(0),
            long_name: row.get(1),
            description: row.get(2),
            post_number: row.get(3),
            active_threads: row.get(4),
        };
        Ok(Some(board))
    }
}

pub fn get_post_number(pool: Pool, board_name: &str) -> Result<i64> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("UPDATE boards SET post_number = post_number + 1 WHERE short_name = $1 \
                    RETURNING post_number",
                   &[&board_name])?;
    Ok(rows.get(0).get(0))
}

pub fn board_exists(pool: Pool, board_name: &str) -> Result<bool> {
    let conn = pool.get().unwrap();

    let rows = conn.query("SELECT short_name FROM boards WHERE short_name = $1",
               &[&board_name])?;
    Ok(!rows.is_empty())
}

pub fn create_thread(pool: Pool, thread: Post) -> Result<()> {
    let conn = pool.get().unwrap();
    let trans = conn.transaction()?;

    trans.execute("INSERT INTO posts (post_number, board, subject, name, email, content, thread, \
                  parent) VALUES ($1, $2, $3, $4, $5, $6, $7, $8);",
                 &[&thread.post_number,
                   &thread.board,
                   &thread.subject,
                   &thread.name,
                   &thread.email,
                   &thread.content,
                   &thread.thread,
                   &thread.parent])?;

    trans.execute("UPDATE boards SET active_threads = $1::BIGINT || active_threads WHERE \
                  short_name = $2",
                 &[&thread.post_number, &thread.board])?;

    trans.commit()?;
    Ok(())
}

pub fn get_thread(pool: Pool, board_name: &str, thread_number: i64) -> Result<Option<Post>> {
    let conn = pool.get().unwrap();

    let rows =
        conn.query("SELECT post_number, board, subject, name, email, content, thread, parent \
                    FROM posts WHERE board = $1 AND post_number = $2",
                   &[&board_name, &thread_number])?;

    if rows.is_empty() {
        Ok(None)
    } else {
        let row = rows.get(0);
        let thread = Post {
            post_number: row.get(0),
            board: row.get(1),
            subject: row.get(2),
            name: row.get(3),
            email: row.get(4),
            content: row.get(5),
            thread: row.get(6),
            parent: row.get(7),
        };
        Ok(Some(thread))
    }
}
<|endoftext|>


<commit_before>use syntax::{abi, ast, ast_util, codemap, parse};
use syntax::ptr::P;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;

static BEFORE_EACH: &'static str = "before_each";
static AFTER_EACH:  &'static str = "after_each";
static BEFORE:      &'static str = "before";
static AFTER:       &'static str = "after";
static IT:          &'static str = "it";

/// Defines the state of a `describe!` macro as it is parsing.
struct DescribeState {
    name: Option<String>,
    before: Option<P<ast::Block>>,
    after: Option<P<ast::Block>>,
    before_each: Option<P<ast::Block>>,
    after_each: Option<P<ast::Block>>,
    tests: Vec<Test>
}

/// A test as a description and associated block.
struct Test {
    description: String,
    block: P<ast::Block>
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe(cx: &mut base::ExtCtxt, _: codemap::Span, tokens: &[ast::TokenTree]) -> Box<base::MacResult + 'static> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state = parse_describe(DescribeState {
        name: None, before: None, after: None,
        before_each: None, after_each: None, tests: vec![]
    }, parse::tts_to_parser(cx.parse_sess(), tokens.to_vec(), cx.cfg()));

    let name = cx.ident_of(state.name.clone().unwrap().replace(" ", "_").as_slice());

    // Create tests from a full DescribeState
    let tests = create_tests(state, cx);

    // Get a glob import of all items in scope to the module that `describe!` is called from.
    let super_glob = cx.view_use_glob(codemap::DUMMY_SP, ast::Inherited, vec![cx.ident_of("super")]);

    // Generate the new module.
    let test_mod = cx.item_mod(codemap::DUMMY_SP, codemap::DUMMY_SP, name, vec![], vec![super_glob], tests);

    // Export the new module.
    base::MacItems::new(vec![test_mod].into_iter())
}

fn create_tests(state: DescribeState, cx: &mut base::ExtCtxt) -> Vec<P<ast::Item>> {
    // FIXME(reem): Implement before and after.
    let (_before, _after) = (state.before, state.after);

    let (before_each, after_each) = (state.before_each, state.after_each);
    let tests = state.tests;

    // Create the #[test] attribute.
    let test_attribute = cx.attribute(codemap::DUMMY_SP,
                                      cx.meta_word(codemap::DUMMY_SP, parse::token::InternedString::new("test")));

    tests.into_iter().map(|Test { description, block }| {
        // Create the full test body by splicing in the statements and view items of the before and
        // after blocks if they are present.
        let test_body = match (&before_each, &after_each) {
            (&None, &None) => block,

            (&Some(ref before), &None) => {
                P(ast::Block {
                    view_items: before.view_items + block.view_items,
                    stmts: before.stmts + block.stmts,
                    ..block.deref().clone()
                })
            },

            (&None, &Some(ref after)) => {
                P(ast::Block {
                    view_items: block.view_items + after.view_items,
                    stmts: block.stmts + after.stmts,
                    ..block.deref().clone()
                })
            },

            (&Some(ref before), &Some(ref after)) => {
                P(ast::Block {
                    view_items: before.view_items + block.view_items + after.view_items,
                    stmts: before.stmts + block.stmts + after.stmts,
                    ..block.deref().clone()
                })
            }
        };

        // Create the final Item that represents the test.
        P(ast::Item {
            // Name it with a snake_case version of the description.
            ident: cx.ident_of(description.replace(" ", "_").as_slice()),

            // Add #[test]
            attrs: vec![test_attribute.clone()],
            id: ast::DUMMY_NODE_ID,
            node: ast::ItemFn(
                // Takes no arguments and returns ()
                cx.fn_decl(vec![], cx.ty_nil()),

                // All the usual types.
                ast::NormalFn,
                abi::Rust,
                ast_util::empty_generics(),

                // Add the body of the function.
                test_body
            ),
            // Inherited visibility (not pub)
            vis: ast::Inherited,
            span: codemap::DUMMY_SP
        })
    }).collect()
}

fn parse_describe(mut state: DescribeState, mut parser: parse::parser::Parser) -> DescribeState {
    // First parse the name of this describe block:
    let (name, _) = parser.parse_str();
    state.name = Some(name.get().to_string());

    // Move past the opening {
    if parse::token::LBRACE != parser.bump_and_get() {
        parser.fatal("Expected { after the description of a describe! block.");
    }

    // Now parse all tests and subsections:
    while parser.token != parse::token::RBRACE {
        // Get the name of this block, must be either:
        //     - before_each
        //     - after_each
        //     - before
        //     - after
        //     - it
        //
        // Any other top-level idents are not allowed.
        let block_name = parser.parse_ident();

        match block_name.as_str() {
            BEFORE_EACH => {
                if state.before_each.is_some() { parser.fatal("Only one `before_each` block is allowed per `describe!` block.") }
                state.before_each = Some(parser.parse_block());
            },

            AFTER_EACH => {
                if state.after_each.is_some() { parser.fatal("Only one `after_each` block is allowed per `describe!` block.") }
                state.after_each = Some(parser.parse_block());
            },

            BEFORE => {
                if state.before.is_some() { parser.fatal("Only one `before` block is allowed per `describe!` block.") }
                state.before = Some(parser.parse_block());
            },

            AFTER => {
                if state.after.is_some() { parser.fatal("Only one `after` block is allowed per `describe!` block.") }
                state.after = Some(parser.parse_block());
            },

            IT => {
                // Description of this `it` block.
                let (description, _) = parser.parse_str();

                state.tests.push(Test {
                    // Get as a String
                    description: description.get().to_string(),

                    // The associated block
                    block: parser.parse_block()
                });
            },

            banned => {
                // Illegal block name.
                let span = parser.span;
                parser.span_fatal(span, format!("Expected one of: `{}`, but found: `{}`",
                    format!("{}, {}, {}, {}, {}", BEFORE_EACH, AFTER_EACH, BEFORE, AFTER, IT).as_slice(),
                    banned).as_slice());
            }
        }
    }

    state
}

<commit_msg>(feat) Add support for arbitrarily nested describe! blocks.<commit_after>use syntax::{abi, ast, ast_util, codemap, parse};
use syntax::parse::token;
use syntax::ptr::P;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;

static BEFORE_EACH: &'static str = "before_each";
static AFTER_EACH:  &'static str = "after_each";
static BEFORE:      &'static str = "before";
static AFTER:       &'static str = "after";
static IT:          &'static str = "it";
static DESCRIBE:    &'static str = "describe";

/// Defines the state of a `describe!` macro as it is parsing.
struct DescribeState {
    name: Option<String>,
    before: Option<P<ast::Block>>,
    after: Option<P<ast::Block>>,
    before_each: Option<P<ast::Block>>,
    after_each: Option<P<ast::Block>>,
    subblocks: Vec<SubBlock>
}

enum SubBlock {
    TestBlock(Test),
    DescribeBlock(P<ast::Item>)
}

/// A test as a description and associated block.
struct Test {
    description: String,
    block: P<ast::Block>
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe(cx: &mut base::ExtCtxt, _: codemap::Span, tokens: &[ast::TokenTree]) -> Box<base::MacResult + 'static> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state = parse_describe(&mut parse::tts_to_parser(cx.parse_sess(), tokens.to_vec(), cx.cfg()), cx);

    // Export the new module.
    base::MacItems::new(Some(create_describe_item(state, cx)).into_iter())
}

fn create_describe_item(state: DescribeState, cx: &mut base::ExtCtxt) -> P<ast::Item> {
    // Get the name of the module from the state.
    let name = cx.ident_of(state.name.clone().unwrap().replace(" ", "_").as_slice());

    // Create subblocks from a full DescribeState
    let subblocks = create_subblocks(state, cx);

    // Get a glob import of all items in scope to the module that `describe!` is called from.
    let super_glob = cx.view_use_glob(codemap::DUMMY_SP, ast::Public, vec![cx.ident_of("super")]);

    // Generate the new module.
    cx.item_mod(codemap::DUMMY_SP, codemap::DUMMY_SP, name, vec![], vec![super_glob], subblocks)
}

fn create_subblocks(state: DescribeState, cx: &mut base::ExtCtxt) -> Vec<P<ast::Item>> {
    // FIXME(reem): Implement before and after.
    let (_before, _after) = (state.before, state.after);

    let (before_each, after_each) = (state.before_each, state.after_each);
    let subblocks = state.subblocks;

    // Create the #[test] attribute.
    let test_attribute = cx.attribute(codemap::DUMMY_SP,
                                      cx.meta_word(codemap::DUMMY_SP, token::InternedString::new("test")));

    subblocks.into_iter().map(|block| {
        match block {
            TestBlock(Test { description, block }) => {
                // Create the full test body by splicing in the statements and view items of the before and
                // after blocks if they are present.
                let test_body = match (&before_each, &after_each) {
                    (&None, &None) => block,

                    (&Some(ref before), &None) => {
                        P(ast::Block {
                            view_items: before.view_items + block.view_items,
                            stmts: before.stmts + block.stmts,
                            ..block.deref().clone()
                        })
                    },

                    (&None, &Some(ref after)) => {
                        P(ast::Block {
                            view_items: block.view_items + after.view_items,
                            stmts: block.stmts + after.stmts,
                            ..block.deref().clone()
                        })
                    },

                    (&Some(ref before), &Some(ref after)) => {
                        P(ast::Block {
                            view_items: before.view_items + block.view_items + after.view_items,
                            stmts: before.stmts + block.stmts + after.stmts,
                            ..block.deref().clone()
                        })
                    }
                };

                // Create the final Item that represents the test.
                P(ast::Item {
                    // Name it with a snake_case version of the description.
                    ident: cx.ident_of(description.replace(" ", "_").as_slice()),

                    // Add #[test]
                    attrs: vec![test_attribute.clone()],
                    id: ast::DUMMY_NODE_ID,
                    node: ast::ItemFn(
                        // Takes no arguments and returns ()
                        cx.fn_decl(vec![], cx.ty_nil()),

                        // All the usual types.
                        ast::NormalFn,
                        abi::Rust,
                        ast_util::empty_generics(),

                        // Add the body of the function.
                        test_body
                    ),
                    // Inherited visibility (not pub)
                    vis: ast::Inherited,
                    span: codemap::DUMMY_SP
                })
            },
            DescribeBlock(item) => item
        }
    }).collect()
}

fn parse_describe(parser: &mut parse::parser::Parser, cx: &mut base::ExtCtxt) -> DescribeState {
    let mut state = DescribeState {
        name: None, before: None, after: None,
        before_each: None, after_each: None, subblocks: vec![]
    };

    // First parse the name of this describe block:
    let (name, _) = parser.parse_str();
    state.name = Some(name.get().to_string());

    // Move past the opening {
    if token::LBRACE != parser.bump_and_get() {
        parser.fatal("Expected { after the description of a describe! block.");
    }

    // Now parse all tests and subsections:
    while parser.token != token::RBRACE {
        // Get the name of this block, must be either:
        //     - before_each
        //     - after_each
        //     - before
        //     - after
        //     - it
        //
        // Any other top-level idents are not allowed.
        let block_name = parser.parse_ident();

        match block_name.as_str() {
            BEFORE_EACH => {
                if state.before_each.is_some() { parser.fatal("Only one `before_each` block is allowed per `describe!` block.") }
                state.before_each = Some(parser.parse_block());
            },

            AFTER_EACH => {
                if state.after_each.is_some() { parser.fatal("Only one `after_each` block is allowed per `describe!` block.") }
                state.after_each = Some(parser.parse_block());
            },

            BEFORE => {
                if state.before.is_some() { parser.fatal("Only one `before` block is allowed per `describe!` block.") }
                state.before = Some(parser.parse_block());
            },

            AFTER => {
                if state.after.is_some() { parser.fatal("Only one `after` block is allowed per `describe!` block.") }
                state.after = Some(parser.parse_block());
            },

            IT => {
                // Description of this `it` block.
                let (description, _) = parser.parse_str();

                state.subblocks.push(TestBlock(Test {
                    // Get as a String
                    description: description.get().to_string(),

                    // The associated block
                    block: parser.parse_block()
                }));
            },

            // Sub `describe!` block.
            DESCRIBE => {
                // Skip over the ! and (
                match (parser.bump_and_get(), parser.bump_and_get()) {
                    (token::NOT, token::LPAREN) => {},
                    (one, two) => parser.fatal(format!("Expected describe!( but found `describe{}{}`", one, two).as_slice())
                };

                // Parse this sublock, generate new item.
                state.subblocks.push(DescribeBlock(create_describe_item(parse_describe(parser, cx), cx)));

                // Move past closing bracket and paren.
                //
                // This has to go in here because it two is EOF on the highest-level invocation.
                match (parser.bump_and_get(), parser.bump_and_get()) {
                    (token::RBRACE, token::RPAREN) => {},
                    (one, two) => parser.fatal(format!("Expected }}) to close `describe!` but found: `{}{}`", one, two).as_slice())
                }
            }

            banned => {
                // Illegal block name.
                let span = parser.span;
                parser.span_fatal(span, format!("Expected one of: `{}`, but found: `{}`",
                    format!("{}, {}, {}, {}, {}", BEFORE_EACH, AFTER_EACH, BEFORE, AFTER, IT).as_slice(),
                    banned).as_slice());
            }
        }
    }

    state
}

<|endoftext|>


<commit_before>//! Free functions that create iterator adaptors or call iterator methods.
//!
//! The benefit of free functions is that they accept any `IntoIterator` as
//! argument, so the resulting code may be easier to read.

use std::fmt::Display;
use std::iter::{self, Zip};
use {
    Interleave,
    Itertools,
    KMerge,
    Merge,
    ZipEq,
};

/// Iterate `iterable` with a running index.
///
/// `IntoIterator` enabled version of `.enumerate()`.
///
/// ```
/// use itertools::enumerate;
///
/// for (i, elt) in enumerate(&[1, 2, 3]) {
///     /* loop body */
/// }
/// ```
pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
    where I: IntoIterator
{
    iterable.into_iter().enumerate()
}

/// Iterate `iterable` in reverse.
///
/// `IntoIterator` enabled version of `.rev()`.
///
/// ```
/// use itertools::rev;
///
/// for elt in rev(&[1, 2, 3]) {
///     /* loop body */
/// }
/// ```
pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
    where I: IntoIterator,
          I::IntoIter: DoubleEndedIterator
{
    iterable.into_iter().rev()
}

/// Iterate `i` and `j` in lock step.
///
/// `IntoIterator` enabled version of `i.zip(j)`.
///
/// ```
/// use itertools::free::zip;
///
/// let data = [1, 2, 3, 4, 5];
/// for (a, b) in zip(&data, &data[1..]) {
///     /* loop body */
/// }
/// ```
pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
    where I: IntoIterator,
          J: IntoIterator
{
    i.into_iter().zip(j)
}

/// Iterate `i` and `j` in lock step.
///
/// **Panics** if the iterators are not of the same length.
///
/// `IntoIterator` enabled version of `i.zip_eq(j)`.
///
/// ```
/// use itertools::free::zip_eq;
///
/// let data = [1, 2, 3, 4, 5];
/// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) {
///     /* loop body */
/// }
/// ```
pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
    where I: IntoIterator,
          J: IntoIterator
{
    i.into_iter().zip_eq(j)
}

/*
/// Create an iterator that first iterates `i` and then `j`.
///
/// `IntoIterator` enabled version of `i.chain(j)`.
///
/// ```
/// use itertools::free::chain;
///
/// for elt in chain(&[1, 2, 3], &[4]) {
///     /* loop body */
/// }
/// ```
pub fn chain<I, J>(i: I, j: J) -> iter::Chain<I::IntoIter, J::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>
{
    i.into_iter().chain(j)
}
*/

/// Perform a fold operation over the iterable.
///
/// `IntoIterator` enabled version of `i.fold(init, f)`
///
/// ```
/// use itertools::free::fold;
///
/// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
/// ```
pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
    where I: IntoIterator,
          F: FnMut(B, I::Item) -> B
{
    iterable.into_iter().fold(init, f)
}

/// Test whether the predicate holds for all elements in the iterable.
///
/// `IntoIterator` enabled version of `i.all(f)`
///
/// ```
/// use itertools::free::all;
///
/// assert!(all(&[1, 2, 3], |elt| *elt > 0));
/// ```
pub fn all<I, F>(iterable: I, f: F) -> bool
    where I: IntoIterator,
          F: FnMut(I::Item) -> bool
{
    iterable.into_iter().all(f)
}

/// Test whether the predicate holds for any elements in the iterable.
///
/// `IntoIterator` enabled version of `i.any(f)`
///
/// ```
/// use itertools::free::any;
///
/// assert!(any(&[0, -1, 2], |elt| *elt > 0));
/// ```
pub fn any<I, F>(iterable: I, f: F) -> bool
    where I: IntoIterator,
          F: FnMut(I::Item) -> bool
{
    iterable.into_iter().any(f)
}

/// Return the maximum value of the iterable.
///
/// `IntoIterator` enabled version of `i.max()`.
///
/// ```
/// use itertools::free::max;
///
/// assert_eq!(max(0..10), Some(9));
/// ```
pub fn max<I>(iterable: I) -> Option<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().max()
}

/// Return the minimum value of the iterable.
///
/// `IntoIterator` enabled version of `i.min()`.
///
/// ```
/// use itertools::free::min;
///
/// assert_eq!(min(0..10), Some(0));
/// ```
pub fn min<I>(iterable: I) -> Option<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().min()
}

/// Create an iterator that interleaves elements in `i` and `j`.
///
/// `IntoIterator` enabled version of `i.interleave(j)`.
///
/// ```
/// use itertools::free::interleave;
///
/// for elt in interleave(&[1, 2, 3], &[2, 3, 4]) {
///     /* loop body */
/// }
/// ```
pub fn interleave<I, J>(i: I, j: J) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>
{
    i.into_iter().interleave(j)
}

/// Create an iterator that merges elements in `i` and `j`.
///
/// `IntoIterator` enabled version of `i.merge(j)`.
///
/// ```
/// use itertools::free::merge;
///
/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
///     /* loop body */
/// }
/// ```
pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>,
          I::Item: PartialOrd
{
    i.into_iter().merge(j)
}

/// Create an iterator that merges elements of the contained iterators.
///
/// Equivalent to `i.into_iter().kmerge()`.
///
/// ```
/// use itertools::free::kmerge;
///
/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
///     /* loop body */
/// }
/// ```
pub fn kmerge<I>(i: I) -> KMerge<<<I as IntoIterator>::Item as IntoIterator>::IntoIter>
    where I: IntoIterator,
          I::Item: IntoIterator,
          <<I as IntoIterator>::Item as IntoIterator>::Item: Ord,
{
    i.into_iter().kmerge()
}

/// Combine all iterator elements into one String, seperated by `sep`.
///
/// `IntoIterator` enabled version of `iterable.join(sep)`.
///
/// ```
/// use itertools::free::join;
///
/// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3");
/// ```
pub fn join<I>(iterable: I, sep: &str) -> String
    where I: IntoIterator,
          I::Item: Display
{
    iterable.into_iter().join(sep)
}

/// Collect all the iterable's elements into a sorted vector in ascending order.
///
/// `IntoIterator` enabled version of `iterable.sorted()`.
///
/// ```
/// use itertools::free::sorted;
/// use itertools::assert_equal;
///
/// assert_equal(sorted("rust".chars()), "rstu".chars());
/// ```
pub fn sorted<I>(iterable: I) -> Vec<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().sorted()
}
<commit_msg>Re-add chain with a workaround for the nightly regression<commit_after>//! Free functions that create iterator adaptors or call iterator methods.
//!
//! The benefit of free functions is that they accept any `IntoIterator` as
//! argument, so the resulting code may be easier to read.

use std::fmt::Display;
use std::iter::{self, Zip};
use {
    Interleave,
    Itertools,
    KMerge,
    Merge,
    ZipEq,
};

/// Iterate `iterable` with a running index.
///
/// `IntoIterator` enabled version of `.enumerate()`.
///
/// ```
/// use itertools::enumerate;
///
/// for (i, elt) in enumerate(&[1, 2, 3]) {
///     /* loop body */
/// }
/// ```
pub fn enumerate<I>(iterable: I) -> iter::Enumerate<I::IntoIter>
    where I: IntoIterator
{
    iterable.into_iter().enumerate()
}

/// Iterate `iterable` in reverse.
///
/// `IntoIterator` enabled version of `.rev()`.
///
/// ```
/// use itertools::rev;
///
/// for elt in rev(&[1, 2, 3]) {
///     /* loop body */
/// }
/// ```
pub fn rev<I>(iterable: I) -> iter::Rev<I::IntoIter>
    where I: IntoIterator,
          I::IntoIter: DoubleEndedIterator
{
    iterable.into_iter().rev()
}

/// Iterate `i` and `j` in lock step.
///
/// `IntoIterator` enabled version of `i.zip(j)`.
///
/// ```
/// use itertools::free::zip;
///
/// let data = [1, 2, 3, 4, 5];
/// for (a, b) in zip(&data, &data[1..]) {
///     /* loop body */
/// }
/// ```
pub fn zip<I, J>(i: I, j: J) -> Zip<I::IntoIter, J::IntoIter>
    where I: IntoIterator,
          J: IntoIterator
{
    i.into_iter().zip(j)
}

/// Iterate `i` and `j` in lock step.
///
/// **Panics** if the iterators are not of the same length.
///
/// `IntoIterator` enabled version of `i.zip_eq(j)`.
///
/// ```
/// use itertools::free::zip_eq;
///
/// let data = [1, 2, 3, 4, 5];
/// for (a, b) in zip_eq(&data[..data.len() - 1], &data[1..]) {
///     /* loop body */
/// }
/// ```
pub fn zip_eq<I, J>(i: I, j: J) -> ZipEq<I::IntoIter, J::IntoIter>
    where I: IntoIterator,
          J: IntoIterator
{
    i.into_iter().zip_eq(j)
}

/// Create an iterator that first iterates `i` and then `j`.
///
/// `IntoIterator` enabled version of `i.chain(j)`.
///
/// ```
/// use itertools::free::chain;
///
/// for elt in chain(&[1, 2, 3], &[4]) {
///     /* loop body */
/// }
/// ```
pub fn chain<I, J>(i: I, j: J) -> iter::Chain<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>
{
    i.into_iter().chain(j)
}

/// Perform a fold operation over the iterable.
///
/// `IntoIterator` enabled version of `i.fold(init, f)`
///
/// ```
/// use itertools::free::fold;
///
/// assert_eq!(fold(&[1., 2., 3.], 0., |a, &b| f32::max(a, b)), 3.);
/// ```
pub fn fold<I, B, F>(iterable: I, init: B, f: F) -> B
    where I: IntoIterator,
          F: FnMut(B, I::Item) -> B
{
    iterable.into_iter().fold(init, f)
}

/// Test whether the predicate holds for all elements in the iterable.
///
/// `IntoIterator` enabled version of `i.all(f)`
///
/// ```
/// use itertools::free::all;
///
/// assert!(all(&[1, 2, 3], |elt| *elt > 0));
/// ```
pub fn all<I, F>(iterable: I, f: F) -> bool
    where I: IntoIterator,
          F: FnMut(I::Item) -> bool
{
    iterable.into_iter().all(f)
}

/// Test whether the predicate holds for any elements in the iterable.
///
/// `IntoIterator` enabled version of `i.any(f)`
///
/// ```
/// use itertools::free::any;
///
/// assert!(any(&[0, -1, 2], |elt| *elt > 0));
/// ```
pub fn any<I, F>(iterable: I, f: F) -> bool
    where I: IntoIterator,
          F: FnMut(I::Item) -> bool
{
    iterable.into_iter().any(f)
}

/// Return the maximum value of the iterable.
///
/// `IntoIterator` enabled version of `i.max()`.
///
/// ```
/// use itertools::free::max;
///
/// assert_eq!(max(0..10), Some(9));
/// ```
pub fn max<I>(iterable: I) -> Option<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().max()
}

/// Return the minimum value of the iterable.
///
/// `IntoIterator` enabled version of `i.min()`.
///
/// ```
/// use itertools::free::min;
///
/// assert_eq!(min(0..10), Some(0));
/// ```
pub fn min<I>(iterable: I) -> Option<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().min()
}

/// Create an iterator that interleaves elements in `i` and `j`.
///
/// `IntoIterator` enabled version of `i.interleave(j)`.
///
/// ```
/// use itertools::free::interleave;
///
/// for elt in interleave(&[1, 2, 3], &[2, 3, 4]) {
///     /* loop body */
/// }
/// ```
pub fn interleave<I, J>(i: I, j: J) -> Interleave<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>
{
    i.into_iter().interleave(j)
}

/// Create an iterator that merges elements in `i` and `j`.
///
/// `IntoIterator` enabled version of `i.merge(j)`.
///
/// ```
/// use itertools::free::merge;
///
/// for elt in merge(&[1, 2, 3], &[2, 3, 4]) {
///     /* loop body */
/// }
/// ```
pub fn merge<I, J>(i: I, j: J) -> Merge<<I as IntoIterator>::IntoIter, <J as IntoIterator>::IntoIter>
    where I: IntoIterator,
          J: IntoIterator<Item = I::Item>,
          I::Item: PartialOrd
{
    i.into_iter().merge(j)
}

/// Create an iterator that merges elements of the contained iterators.
///
/// Equivalent to `i.into_iter().kmerge()`.
///
/// ```
/// use itertools::free::kmerge;
///
/// for elt in kmerge(vec![vec![0, 2, 4], vec![1, 3, 5], vec![6, 7]]) {
///     /* loop body */
/// }
/// ```
pub fn kmerge<I>(i: I) -> KMerge<<<I as IntoIterator>::Item as IntoIterator>::IntoIter>
    where I: IntoIterator,
          I::Item: IntoIterator,
          <<I as IntoIterator>::Item as IntoIterator>::Item: Ord,
{
    i.into_iter().kmerge()
}

/// Combine all iterator elements into one String, seperated by `sep`.
///
/// `IntoIterator` enabled version of `iterable.join(sep)`.
///
/// ```
/// use itertools::free::join;
///
/// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3");
/// ```
pub fn join<I>(iterable: I, sep: &str) -> String
    where I: IntoIterator,
          I::Item: Display
{
    iterable.into_iter().join(sep)
}

/// Collect all the iterable's elements into a sorted vector in ascending order.
///
/// `IntoIterator` enabled version of `iterable.sorted()`.
///
/// ```
/// use itertools::free::sorted;
/// use itertools::assert_equal;
///
/// assert_equal(sorted("rust".chars()), "rstu".chars());
/// ```
pub fn sorted<I>(iterable: I) -> Vec<I::Item>
    where I: IntoIterator,
          I::Item: Ord
{
    iterable.into_iter().sorted()
}
<|endoftext|>


<commit_before>use toml::Table;
use walkdir::WalkDir;
use std::path::Path;
use std::os::unix::fs::symlink;
use std::fs::{create_dir_all, copy, symlink_metadata, read_link};

pub fn load(home_dir: &str, app_dir: &str, config: &Table) {
    let dotfiles = config
        .get("dotfiles")
        .expect("No [dotfiles] section found")
        .as_table().expect("[templates] is not a valid TOML table");

    for (dotfile, tar_path) in dotfiles {
        let tar_path = tar_path.as_str().expect("error: path not a valid string");
        let tar_path = tar_path.replace("$HOME", home_dir).replace("~", home_dir);
        let src_path = format!("{}/dotfiles/{}", app_dir, dotfile);
        let src_len = src_path.len();

        for entry in WalkDir::new(&src_path) {
            let ent_src_path = entry
                .expect("There was an error accessing the dotfile source.");
            let ent_src_path = ent_src_path.path().to_str()
                .expect("Path is not a valid string.");
            let ent_tar_path = format!("{}{}", tar_path, &ent_src_path[src_len..]);

            println!("Copying {} to {}", ent_src_path, ent_tar_path);

            let src_meta = symlink_metadata(&ent_src_path)
                .expect("Could not get file metadata.");
            if src_meta.is_file() || src_meta.file_type().is_symlink() {
                create_dir_all(Path::new(&ent_tar_path).parent()
                               .expect("Invalid path structure."))
                    .expect("Could not create target directory.");
            }
            if src_meta.is_file() {
                copy(&ent_src_path, &ent_tar_path)
                    .expect(format!("Could not copy {}.", &ent_src_path).as_str());
            }
            else if src_meta.file_type().is_symlink() {
                let real_path = read_link(&ent_src_path)
                    .expect("Could not resolve symlink path.");
                symlink(real_path, &ent_tar_path)
                    .expect(format!("Could not copy {}.", &ent_src_path).as_str());
            }
        }
    }
}
<commit_msg>Now removing symlinks before trying to create them.<commit_after>use toml::Table;
use walkdir::WalkDir;
use std::path::Path;
use std::os::unix::fs::symlink;
use std::fs::{remove_file, create_dir_all, copy, symlink_metadata, read_link};

pub fn load(home_dir: &str, app_dir: &str, config: &Table) {
    let dotfiles = config
        .get("dotfiles")
        .expect("No [dotfiles] section found")
        .as_table().expect("[templates] is not a valid TOML table");

    for (dotfile, tar_path) in dotfiles {
        let tar_path = tar_path.as_str().expect("error: path not a valid string");
        let tar_path = tar_path.replace("$HOME", home_dir).replace("~", home_dir);
        let src_path = format!("{}/dotfiles/{}", app_dir, dotfile);
        let src_len = src_path.len();

        for entry in WalkDir::new(&src_path) {
            let ent_src_path = entry
                .expect("There was an error accessing the dotfile source.");
            let ent_src_path = ent_src_path.path().to_str()
                .expect("Path is not a valid string.");
            let ent_tar_path = format!("{}{}", tar_path, &ent_src_path[src_len..]);

            println!("Copying {} to {}", ent_src_path, ent_tar_path);

            let src_meta = symlink_metadata(&ent_src_path)
                .expect("Could not get file metadata.");
            if src_meta.is_file() || src_meta.file_type().is_symlink() {
                create_dir_all(Path::new(&ent_tar_path).parent()
                               .expect("Invalid path structure."))
                    .expect("Could not create target directory.");
            }
            if src_meta.is_file() {
                copy(&ent_src_path, &ent_tar_path)
                    .expect(format!("Could not copy {}.", &ent_src_path).as_str());
            }
            else if src_meta.file_type().is_symlink() {
                let _ = remove_file(&ent_tar_path);
                let real_path = read_link(&ent_src_path)
                    .expect("Could not resolve symlink path.");
                symlink(real_path, &ent_tar_path)
                    .expect(format!("Could not copy {}.", &ent_src_path).as_str());
            }
        }
    }
}
<|endoftext|>


<commit_before>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};

const DOWNLOAD_BUFFER_BYTES: usize = 1024*64;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(500);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;

        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes-last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes-last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = true;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.set_units(Units::Bytes);
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<commit_msg>Create constant for progress update interval.<commit_after>use {Bytes, Chunk, SChunks};
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};

const DOWNLOAD_BUFFER_BYTES: usize = 1024*64;
const PROGRESS_UPDATE_INTERVAL_MILLIS : u32 = 500;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, Bytes)> {

    match get_chunk_length(chunk_index, content_length, global_chunk_length) {
        Some(range) => {
            let mut header = Headers::new();
            header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
            Some((header, range.1 - range.0))
        }
        None => None,
    }

}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    chunk_vector: &mut Chunk,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>)
                    -> Result<Bytes, Error> {

    let mut body = http_client.get_http_response_using_headers(url, http_header).unwrap();
    if !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(PROGRESS_UPDATE_INTERVAL_MILLIS);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_vector.extend_from_slice(&bytes_buffer[..n]);
        sum_bytes += n as u64;

        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes-last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes-last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server.
pub fn download_chunks(content_length: u64,
                       downloaded_chunks: &SChunks,
                       nb_chunks: u64,
                       url: &str) {

    // let mut downloaded_chunks: Arc<Mutex<Chunks>> =
    //     Arc::new(Mutex::new(Chunks::with_capacity(nb_chunks as usize)));
    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (http_header, chunk_length) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let mut chunk_content = Chunk::with_capacity(chunk_length as usize);
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        let clone_chunks = downloaded_chunks.clone();

        // Progress bar customization
        let mut mp = mpb.create_bar(chunk_length);
        mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        mp.format("|#--|");
        mp.show_tick = true;
        mp.show_speed = true;
        mp.show_percent = true;
        mp.show_counter = false;
        mp.show_time_left = true;
        mp.set_units(Units::Bytes);
        mp.message(&format!("Chunk {} ", chunk_index));

        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                                    http_header,
                                                                    &mut chunk_content,
                                                                    &url_clone,
                                                                    &mut mp) {
            Ok(bytes_written) => {
                if bytes_written > 0 {
                    let mut shared_clone_chunks = clone_chunks.lock().unwrap();
                    shared_clone_chunks.insert(chunk_index as usize, chunk_content);
                    mp.finish();
                } else {
                    panic!("The downloaded chunk {} is empty", chunk_index);
                }
            }
            Err(error) => {
                panic!("Cannot download the chunk {}, due to error {}",
                       chunk_index,
                       error);
            }
        }));
    }

    mpb.listen();

    for child in jobs {
        let _ = child.join();
    }

}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::get_header_from_index;
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, 247)), get_header_from_index(3, 998, 250));
    }

}
<|endoftext|>


<commit_before>extern crate time;

use prompt_buffer::escape;
use prompt_buffer::buffer::{PromptBufferPlugin, PluginSpeed};
use prompt_buffer::line::{PromptLine, PromptLineBuilder};
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, File};
use term::color;

pub struct DueDatePlugin;

impl DueDatePlugin {
    pub fn new() -> DueDatePlugin {
        DueDatePlugin
    }
}

struct PathTraversal {
    path: Path
}

impl PathTraversal {
    fn new(p: &Path) -> PathTraversal {
        let mut pat = p.clone();
        pat.push("dummy");
        PathTraversal {
            path: pat,
        }
    }
}

impl Iterator for PathTraversal {
    type Item = Path;

    fn next(&mut self) -> Option<Path> {
        if !self.path.pop() { return None; };
        Some(self.path.clone())
    }
}

struct TimePeriod {
    singular: String,
    plural: String
}

trait ToTimePeriod {
    fn as_period(&self) -> TimePeriod;
}

impl ToTimePeriod for str {
    fn as_period(&self) -> TimePeriod {
        let mut p = self.to_string();
        p.push('s');

        (self, p.as_slice()).as_period()
    }
}

impl<'s> ToTimePeriod for (&'s str, &'s str) {
    fn as_period(&self) -> TimePeriod {
        let (s, p) = *self;
        TimePeriod {
            singular: s.to_string(),
            plural: p.to_string()
        }
    }
}

impl PromptBufferPlugin for DueDatePlugin {
    fn run(&mut self, _: &PluginSpeed, path: &Path, lines: &mut Vec<PromptLine>) {
        for mut path in PathTraversal::new(path) {
            path.push(".due");

            if path.is_file() {
                let mut reader = BufferedReader::new(File::open(&path));

                let mut line = |&mut: s: &str| { reader.read_line().unwrap_or(s.to_string()) };

                match time::strptime(line("").trim().as_slice(), "%a %b %d %H:%M:%S %Y") {
                    Ok(due_date) => {
                        let due = due_date.to_timespec();
                        let now = time::now().to_timespec();

                        let s = due.sec - now.sec;
                        let (seconds, past_due) = if s < 0 { (-s, true) } else { (s, false) };
                        let mut seconds: f32 = seconds as f32;

                        let ups: [f32; 9] = [10.0, 10.0, 10.0, 365.0/30.0, 30.0, 24.0, 60.0, 60.0, 1.0];
                        let time_periods = [
                            ("millenium", "millenia").as_period(),
                            ("century", "centuries").as_period(),
                            "decade".as_period(),
                            "year".as_period(),
                            "month".as_period(),
                            "day".as_period(),
                            "hour".as_period(),
                            "minute".as_period(),
                            "second".as_period(),
                        ];

                        let times = range(0, ups.len()).map(|i| {
                            ups[i..].iter().fold(1.0, |a, &b| a * b)
                        });

                        let accuracy = 2u8;
                        let mut count = 0u8;
                        let mut due_phrase = String::new();

                        for (amount, name) in times.zip(time_periods.iter()) {
                            if seconds > amount {
                                count += 1;
                                let rem = seconds % amount;
                                let amt = seconds / amount - (rem / amount);
                                seconds = rem;
                                let name = if amt > 1.0 { &name.plural } else { &name.singular };
                                due_phrase = format!("{}{} {} ", due_phrase, amt, name);
                            }

                            if count >= accuracy {
                                break;
                            }
                        }

                        let title = line("Project");
                        let future = line("is due in");
                        let past = line("was due");
                        let (color, temporal, postfix) = if past_due {
                            (color::RED, past, " ago")
                        } else {
                            (color::CYAN, future, "")
                        };

                        due_phrase = format!("{}{} {}: {}{}{}",
                            escape::col(color::MAGENTA),
                            title.trim(),
                            temporal.trim(),
                            escape::col(color),
                            due_phrase.trim(),
                            postfix
                        );

                        lines.push(PromptLineBuilder::new()
                            .block(due_phrase)
                            .build());
                    },
                    Err(_) => {}
                }
            }
        }
    }
}
<commit_msg>Round amounts for due_date<commit_after>extern crate time;

use prompt_buffer::escape;
use prompt_buffer::buffer::{PromptBufferPlugin, PluginSpeed};
use prompt_buffer::line::{PromptLine, PromptLineBuilder};
use std::old_io::fs::PathExtensions;
use std::old_io::{BufferedReader, File};
use std::num::Float;
use term::color;

pub struct DueDatePlugin;

impl DueDatePlugin {
    pub fn new() -> DueDatePlugin {
        DueDatePlugin
    }
}

struct PathTraversal {
    path: Path
}

impl PathTraversal {
    fn new(p: &Path) -> PathTraversal {
        let mut pat = p.clone();
        pat.push("dummy");
        PathTraversal {
            path: pat,
        }
    }
}

impl Iterator for PathTraversal {
    type Item = Path;

    fn next(&mut self) -> Option<Path> {
        if !self.path.pop() { return None; };
        Some(self.path.clone())
    }
}

struct TimePeriod {
    singular: String,
    plural: String
}

trait ToTimePeriod {
    fn as_period(&self) -> TimePeriod;
}

impl ToTimePeriod for str {
    fn as_period(&self) -> TimePeriod {
        let mut p = self.to_string();
        p.push('s');

        (self, p.as_slice()).as_period()
    }
}

impl<'s> ToTimePeriod for (&'s str, &'s str) {
    fn as_period(&self) -> TimePeriod {
        let (s, p) = *self;
        TimePeriod {
            singular: s.to_string(),
            plural: p.to_string()
        }
    }
}

impl PromptBufferPlugin for DueDatePlugin {
    fn run(&mut self, _: &PluginSpeed, path: &Path, lines: &mut Vec<PromptLine>) {
        for mut path in PathTraversal::new(path) {
            path.push(".due");

            if path.is_file() {
                let mut reader = BufferedReader::new(File::open(&path));

                let mut line = |&mut: s: &str| { reader.read_line().unwrap_or(s.to_string()) };

                match time::strptime(line("").trim().as_slice(), "%a %b %d %H:%M:%S %Y") {
                    Ok(due_date) => {
                        let due = due_date.to_timespec();
                        let now = time::now().to_timespec();

                        let s = due.sec - now.sec;
                        let (seconds, past_due) = if s < 0 { (-s, true) } else { (s, false) };
                        let mut seconds: f32 = seconds as f32;

                        let ups: [f32; 9] = [10.0, 10.0, 10.0, 365.0/30.0, 30.0, 24.0, 60.0, 60.0, 1.0];
                        let time_periods = [
                            ("millenium", "millenia").as_period(),
                            ("century", "centuries").as_period(),
                            "decade".as_period(),
                            "year".as_period(),
                            "month".as_period(),
                            "day".as_period(),
                            "hour".as_period(),
                            "minute".as_period(),
                            "second".as_period(),
                        ];

                        let times = range(0, ups.len()).map(|i| {
                            ups[i..].iter().fold(1.0, |a, &b| a * b)
                        });

                        let accuracy = 2u8;
                        let mut count = 0u8;
                        let mut due_phrase = String::new();

                        for (amount, name) in times.zip(time_periods.iter()) {
                            if seconds > amount {
                                count += 1;
                                let rem = seconds % amount;
                                let amt = seconds / amount - (rem / amount);
                                seconds = rem;
                                let name = if amt > 1.0 { &name.plural } else { &name.singular };
                                due_phrase = format!("{}{} {} ", due_phrase, amt.round() as i32, name);
                            }

                            if count >= accuracy {
                                break;
                            }
                        }

                        let title = line("Project");
                        let future = line("is due in");
                        let past = line("was due");
                        let (color, temporal, postfix) = if past_due {
                            (color::RED, past, " ago")
                        } else {
                            (color::CYAN, future, "")
                        };

                        due_phrase = format!("{}{} {}: {}{}{}",
                            escape::col(color::MAGENTA),
                            title.trim(),
                            temporal.trim(),
                            escape::col(color),
                            due_phrase.trim(),
                            postfix
                        );

                        lines.push(PromptLineBuilder::new()
                            .block(due_phrase)
                            .build());
                    },
                    Err(_) => {}
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Functions for computing properties of the terminal grid

use std::collections::{vec_deque, VecDeque};
use std::ops::{Index, IndexMut, Deref, DerefMut};
use std::slice::{Iter, IterMut};

use term::Cursor;
use ::Rgb;

/// Calculate the number of cells for an axis
pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32 {
    println!("num_cells_axis(cell_width: {}, cell_sep: {}, screen_width: {}",
             cell_width, cell_sep, screen_width);
    ((screen_width as i32 - cell_sep) as f64 / (cell_width as i32 + cell_sep) as f64) as u32
}

#[derive(Clone, Debug)]
pub struct Cell {
    pub c: char,
    pub fg: Rgb,
    pub bg: Rgb,
    pub flags: CellFlags,
}

bitflags! {
    pub flags CellFlags: u32 {
        const INVERSE   = 0b00000001,
        const BOLD      = 0b00000010,
        const ITALIC    = 0b00000100,
        const UNDERLINE = 0b00001000,
    }
}

impl Cell {
    pub fn new(c: char) -> Cell {
        Cell {
            c: c.into(),
            bg: Default::default(),
            fg: Default::default(),
            flags: CellFlags::empty(),
        }
    }
}

/// Represents the terminal display contents
#[derive(Clone)]
pub struct Grid {
    /// Rows in the grid. Each row holds a list of cells corresponding to the columns in that row.
    raw: VecDeque<Row>,

    /// Number of columns
    cols: usize,

    /// Number of rows.
    ///
    /// Invariant: rows is equivalent to cells.len()
    rows: usize,
}

impl Grid {
    pub fn new(rows: usize, cols: usize) -> Grid {
        let mut raw = VecDeque::with_capacity(rows);
        for _ in 0..raw.capacity() {
            raw.push_back(Row::new(cols));
        }

        Grid {
            raw: raw,
            cols: cols,
            rows: rows,
        }
    }

    pub fn rows(&self) -> vec_deque::Iter<Row> {
        self.raw.iter()
    }

    pub fn rows_mut(&mut self) -> vec_deque::IterMut<Row> {
        self.raw.iter_mut()
    }

    pub fn num_rows(&self) -> usize {
        self.rows
    }

    pub fn num_cols(&self) -> usize {
        self.cols
    }

    pub fn feed(&mut self) {
        // do the borrowck dance
        let row = self.raw.pop_front().unwrap();
        self.raw.push_back(row);
    }

    pub fn unfeed(&mut self) {
        // do the borrowck dance
        let row = self.raw.pop_back().unwrap();
        self.raw.push_front(row);
    }

    pub fn clear(&mut self) {
        for row in self.raw.iter_mut() {
            for cell in row.iter_mut() {
                cell.c = ' ';
            }
        }
    }
}

impl Index<usize> for Grid {
    type Output = Row;

    #[inline]
    fn index<'a>(&'a self, index: usize) -> &'a Row {
        &self.raw[index]
    }
}

impl IndexMut<usize> for Grid {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Row {
        &mut self.raw[index]
    }
}

impl Index<Cursor> for Grid {
    type Output = Cell;

    #[inline]
    fn index<'a>(&'a self, cursor: Cursor) -> &'a Cell {
        &self.raw[cursor.y as usize][cursor.x as usize]
    }
}

impl IndexMut<Cursor> for Grid {
    #[inline]
    fn index_mut<'a>(&'a mut self, cursor: Cursor) -> &'a mut Cell {
        &mut self.raw[cursor.y as usize][cursor.x as usize]
    }
}

/// A row in the grid
#[derive(Debug, Clone)]
pub struct Row(Vec<Cell>);

impl Row {
    pub fn new(columns: usize) -> Row {
        Row(vec![Cell::new(' '); columns])
    }

    pub fn cells(&self) -> Iter<Cell> {
        self.0.iter()
    }

    pub fn cells_mut(&mut self) -> IterMut<Cell> {
        self.0.iter_mut()
    }
}

impl Deref for Row {
    type Target = Vec<Cell>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Row {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl Index<usize> for Row {
    type Output = Cell;

    #[inline]
    fn index<'a>(&'a self, index: usize) -> &'a Cell {
        &self.0[index]
    }
}

impl IndexMut<usize> for Row {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Cell {
        &mut self.0[index]
    }
}
<commit_msg>Fix bug where there were extra grid rows<commit_after>//! Functions for computing properties of the terminal grid

use std::collections::{vec_deque, VecDeque};
use std::ops::{Index, IndexMut, Deref, DerefMut};
use std::slice::{Iter, IterMut};

use term::Cursor;
use ::Rgb;

/// Calculate the number of cells for an axis
pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32 {
    println!("num_cells_axis(cell_width: {}, cell_sep: {}, screen_width: {}",
             cell_width, cell_sep, screen_width);
    ((screen_width as i32 - cell_sep) as f64 / (cell_width as i32 + cell_sep) as f64) as u32
}

#[derive(Clone, Debug)]
pub struct Cell {
    pub c: char,
    pub fg: Rgb,
    pub bg: Rgb,
    pub flags: CellFlags,
}

bitflags! {
    pub flags CellFlags: u32 {
        const INVERSE   = 0b00000001,
        const BOLD      = 0b00000010,
        const ITALIC    = 0b00000100,
        const UNDERLINE = 0b00001000,
    }
}

impl Cell {
    pub fn new(c: char) -> Cell {
        Cell {
            c: c.into(),
            bg: Default::default(),
            fg: Default::default(),
            flags: CellFlags::empty(),
        }
    }
}

/// Represents the terminal display contents
#[derive(Clone)]
pub struct Grid {
    /// Rows in the grid. Each row holds a list of cells corresponding to the columns in that row.
    raw: VecDeque<Row>,

    /// Number of columns
    cols: usize,

    /// Number of rows.
    ///
    /// Invariant: rows is equivalent to cells.len()
    rows: usize,
}

impl Grid {
    pub fn new(rows: usize, cols: usize) -> Grid {
        let mut raw = VecDeque::with_capacity(rows);
        for _ in 0..rows {
            raw.push_back(Row::new(cols));
        }

        Grid {
            raw: raw,
            cols: cols,
            rows: rows,
        }
    }

    pub fn rows(&self) -> vec_deque::Iter<Row> {
        self.raw.iter()
    }

    pub fn rows_mut(&mut self) -> vec_deque::IterMut<Row> {
        self.raw.iter_mut()
    }

    pub fn num_rows(&self) -> usize {
        self.rows
    }

    pub fn num_cols(&self) -> usize {
        self.cols
    }

    pub fn feed(&mut self) {
        // do the borrowck dance
        let row = self.raw.pop_front().unwrap();
        self.raw.push_back(row);
    }

    pub fn unfeed(&mut self) {
        // do the borrowck dance
        let row = self.raw.pop_back().unwrap();
        self.raw.push_front(row);
    }

    pub fn clear(&mut self) {
        for row in self.raw.iter_mut() {
            for cell in row.iter_mut() {
                cell.c = ' ';
            }
        }
    }
}

impl Index<usize> for Grid {
    type Output = Row;

    #[inline]
    fn index<'a>(&'a self, index: usize) -> &'a Row {
        &self.raw[index]
    }
}

impl IndexMut<usize> for Grid {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Row {
        &mut self.raw[index]
    }
}

impl Index<Cursor> for Grid {
    type Output = Cell;

    #[inline]
    fn index<'a>(&'a self, cursor: Cursor) -> &'a Cell {
        &self.raw[cursor.y as usize][cursor.x as usize]
    }
}

impl IndexMut<Cursor> for Grid {
    #[inline]
    fn index_mut<'a>(&'a mut self, cursor: Cursor) -> &'a mut Cell {
        &mut self.raw[cursor.y as usize][cursor.x as usize]
    }
}

/// A row in the grid
#[derive(Debug, Clone)]
pub struct Row(Vec<Cell>);

impl Row {
    pub fn new(columns: usize) -> Row {
        Row(vec![Cell::new(' '); columns])
    }

    pub fn cells(&self) -> Iter<Cell> {
        self.0.iter()
    }

    pub fn cells_mut(&mut self) -> IterMut<Cell> {
        self.0.iter_mut()
    }
}

impl Deref for Row {
    type Target = Vec<Cell>;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for Row {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl Index<usize> for Row {
    type Output = Cell;

    #[inline]
    fn index<'a>(&'a self, index: usize) -> &'a Cell {
        &self.0[index]
    }
}

impl IndexMut<usize> for Row {
    #[inline]
    fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Cell {
        &mut self.0[index]
    }
}
<|endoftext|>