<commit_before>//! Vi command mode specific key bindings
use super::{assert_cursor, assert_history};
use config::EditMode;
use consts::KeyPress;

#[test]
fn dollar() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hi"),
        &[KeyPress::Esc, KeyPress::Char('$'), KeyPress::Enter],
        ("Hi", ""), // FIXME
    );
}

/*#[test]
fn dot() {
    // TODO
}*/

#[test]
fn zero() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('0'), KeyPress::Enter],
        ("", "Hi"),
    );
}

#[test]
fn caret() {
    assert_cursor(
        EditMode::Vi,
        (" Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('^'), KeyPress::Enter],
        (" ", "Hi"),
    );
}

#[test]
fn a() {
    assert_cursor(
        EditMode::Vi,
        ("B", "e"),
        &[
            KeyPress::Esc,
            KeyPress::Char('a'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("By", "e"),
    );
}

#[test]
fn uppercase_a() {
    assert_cursor(
        EditMode::Vi,
        ("", "By"),
        &[
            KeyPress::Esc,
            KeyPress::Char('A'),
            KeyPress::Char('e'),
            KeyPress::Enter,
        ],
        ("Bye", ""),
    );
}

#[test]
fn b() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[KeyPress::Esc, KeyPress::Char('b'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('b'),
            KeyPress::Enter,
        ],
        ("Hello", ", world!"),
    );
}

#[test]
fn uppercase_b() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[KeyPress::Esc, KeyPress::Char('B'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('B'),
            KeyPress::Enter,
        ],
        ("", "Hello, world!"),
    );
}

#[test]
fn ctrl_k() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ""),
        &[KeyPress::Esc, KeyPress::Ctrl('K'), KeyPress::Enter],
        ("H", ""),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hi"),
        &[KeyPress::Esc, KeyPress::Ctrl('K'), KeyPress::Enter],
        ("", ""),
    );
    assert_cursor(
        EditMode::Vi,
        ("By", "e"),
        &[KeyPress::Esc, KeyPress::Ctrl('K'), KeyPress::Enter],
        ("B", ""),
    );
}

#[test]
fn e() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('e'), KeyPress::Enter],
        ("Hell", "o, world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('e'),
            KeyPress::Enter,
        ],
        ("Hello, worl", "d!"),
    );
}

#[test]
fn uppercase_e() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('E'), KeyPress::Enter],
        ("Hello", ", world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('E'),
            KeyPress::Enter,
        ],
        ("Hello, world", "!"),
    );
}

#[test]
fn i() {
    assert_cursor(
        EditMode::Vi,
        ("Be", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('i'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("By", "e"),
    );
}

#[test]
fn uppercase_i() {
    assert_cursor(
        EditMode::Vi,
        ("Be", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('I'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("y", "Be"),
    );
}

#[test]
fn u() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('u'),
            KeyPress::Enter,
        ],
        ("Hello,", " world"),
    );
}

#[test]
fn w() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('w'), KeyPress::Enter],
        ("Hello", ", world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('w'),
            KeyPress::Enter,
        ],
        ("Hello, ", "world!"),
    );
}

#[test]
fn uppercase_w() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('W'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('W'),
            KeyPress::Enter,
        ],
        ("Hello, world", "!"),
    );
}

#[test]
fn x() {
    assert_cursor(
        EditMode::Vi,
        ("", "a"),
        &[KeyPress::Esc, KeyPress::Char('x'), KeyPress::Enter],
        ("", ""),
    );
}

#[test]
fn uppercase_x() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('X'), KeyPress::Enter],
        ("", "i"),
    );
}

#[test]
fn h() {
    for key in &[
        KeyPress::Char('h'),
        KeyPress::Ctrl('H'),
        KeyPress::Backspace,
    ] {
        assert_cursor(
            EditMode::Vi,
            ("Bye", ""),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("B", "ye"),
        );
        assert_cursor(
            EditMode::Vi,
            ("Bye", ""),
            &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter],
            ("", "Bye"),
        );
    }
}

#[test]
fn l() {
    for key in &[KeyPress::Char('l'), KeyPress::Char(' ')] {
        assert_cursor(
            EditMode::Vi,
            ("", "Hi"),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("H", "i"),
        );
        assert_cursor(
            EditMode::Vi,
            ("", "Hi"),
            &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter],
            ("Hi", ""),
        );
    }
}

#[test]
fn j() {
    for key in &[
        KeyPress::Char('j'),
        KeyPress::Char('+'),
        KeyPress::Ctrl('N'),
    ] {
        assert_history(
            EditMode::Vi,
            &["line1", "line2"],
            &[
                KeyPress::Esc,
                KeyPress::Ctrl('P'),
                KeyPress::Ctrl('P'),
                *key,
                KeyPress::Enter,
            ],
            ("line2", ""),
        );
    }
}

#[test]
fn k() {
    for key in &[
        KeyPress::Char('k'),
        KeyPress::Char('-'),
        KeyPress::Ctrl('P'),
    ] {
        assert_history(
            EditMode::Vi,
            &["line1"],
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("line1", ""),
        );
    }
}

#[test]
fn p() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('p'),
            KeyPress::Enter,
        ],
        (" Hello", ",world"),
    );
}

#[test]
fn uppercase_p() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('P'),
            KeyPress::Enter,
        ],
        ("Hello", ", world"),
    );
}
<commit_msg>Tests: vi command mode<commit_after>//! Vi command mode specific key bindings
use super::{assert_cursor, assert_history};
use config::EditMode;
use consts::KeyPress;

#[test]
fn dollar() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hi"),
        &[KeyPress::Esc, KeyPress::Char('$'), KeyPress::Enter],
        ("Hi", ""), // FIXME
    );
}

/*#[test]
fn dot() {
    // TODO
}*/

#[test]
fn semi_colon() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('f'),
            KeyPress::Char('o'),
            KeyPress::Char(';'),
            KeyPress::Enter,
        ],
        ("Hello, w", "orld!"),
    );
}

#[test]
fn comma() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, w", "orld!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('f'),
            KeyPress::Char('l'),
            KeyPress::Char(','),
            KeyPress::Enter,
        ],
        ("Hel", "lo, world!"),
    );
}

#[test]
fn zero() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('0'), KeyPress::Enter],
        ("", "Hi"),
    );
}

#[test]
fn caret() {
    assert_cursor(
        EditMode::Vi,
        (" Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('^'), KeyPress::Enter],
        (" ", "Hi"),
    );
}

#[test]
fn a() {
    assert_cursor(
        EditMode::Vi,
        ("B", "e"),
        &[
            KeyPress::Esc,
            KeyPress::Char('a'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("By", "e"),
    );
}

#[test]
fn uppercase_a() {
    assert_cursor(
        EditMode::Vi,
        ("", "By"),
        &[
            KeyPress::Esc,
            KeyPress::Char('A'),
            KeyPress::Char('e'),
            KeyPress::Enter,
        ],
        ("Bye", ""),
    );
}

#[test]
fn b() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[KeyPress::Esc, KeyPress::Char('b'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('b'),
            KeyPress::Enter,
        ],
        ("Hello", ", world!"),
    );
}

#[test]
fn uppercase_b() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[KeyPress::Esc, KeyPress::Char('B'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('B'),
            KeyPress::Enter,
        ],
        ("", "Hello, world!"),
    );
}

#[test]
fn uppercase_c() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, w", "orld!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('C'),
            KeyPress::Char('i'),
            KeyPress::Enter,
        ],
        ("Hello, i", ""),
    );
}

#[test]
fn ctrl_k() {
    for key in &[KeyPress::Char('D'), KeyPress::Ctrl('K')] {
        assert_cursor(
            EditMode::Vi,
            ("Hi", ""),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("H", ""),
        );
        assert_cursor(
            EditMode::Vi,
            ("", "Hi"),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("", ""),
        );
        assert_cursor(
            EditMode::Vi,
            ("By", "e"),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("B", ""),
        );
    }
}

#[test]
fn e() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('e'), KeyPress::Enter],
        ("Hell", "o, world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('e'),
            KeyPress::Enter,
        ],
        ("Hello, worl", "d!"),
    );
}

#[test]
fn uppercase_e() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('E'), KeyPress::Enter],
        ("Hello", ", world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('E'),
            KeyPress::Enter,
        ],
        ("Hello, world", "!"),
    );
}

#[test]
fn f() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('f'),
            KeyPress::Char('r'),
            KeyPress::Enter,
        ],
        ("Hello, wo", "rld!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('3'),
            KeyPress::Char('f'),
            KeyPress::Char('l'),
            KeyPress::Enter,
        ],
        ("Hello, wor", "ld!"),
    );
}

#[test]
fn uppercase_f() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('F'),
            KeyPress::Char('r'),
            KeyPress::Enter,
        ],
        ("Hello, wo", "rld!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('3'),
            KeyPress::Char('F'),
            KeyPress::Char('l'),
            KeyPress::Enter,
        ],
        ("He", "llo, world!"),
    );
}

#[test]
fn i() {
    assert_cursor(
        EditMode::Vi,
        ("Be", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('i'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("By", "e"),
    );
}

#[test]
fn uppercase_i() {
    assert_cursor(
        EditMode::Vi,
        ("Be", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('I'),
            KeyPress::Char('y'),
            KeyPress::Enter,
        ],
        ("y", "Be"),
    );
}

#[test]
fn u() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('u'),
            KeyPress::Enter,
        ],
        ("Hello,", " world"),
    );
}

#[test]
fn w() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('w'), KeyPress::Enter],
        ("Hello", ", world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('w'),
            KeyPress::Enter,
        ],
        ("Hello, ", "world!"),
    );
}

#[test]
fn uppercase_w() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[KeyPress::Esc, KeyPress::Char('W'), KeyPress::Enter],
        ("Hello, ", "world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('2'),
            KeyPress::Char('W'),
            KeyPress::Enter,
        ],
        ("Hello, world", "!"),
    );
}

#[test]
fn x() {
    assert_cursor(
        EditMode::Vi,
        ("", "a"),
        &[KeyPress::Esc, KeyPress::Char('x'), KeyPress::Enter],
        ("", ""),
    );
}

#[test]
fn uppercase_x() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ""),
        &[KeyPress::Esc, KeyPress::Char('X'), KeyPress::Enter],
        ("", "i"),
    );
}

#[test]
fn h() {
    for key in &[
        KeyPress::Char('h'),
        KeyPress::Ctrl('H'),
        KeyPress::Backspace,
    ] {
        assert_cursor(
            EditMode::Vi,
            ("Bye", ""),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("B", "ye"),
        );
        assert_cursor(
            EditMode::Vi,
            ("Bye", ""),
            &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter],
            ("", "Bye"),
        );
    }
}

#[test]
fn l() {
    for key in &[KeyPress::Char('l'), KeyPress::Char(' ')] {
        assert_cursor(
            EditMode::Vi,
            ("", "Hi"),
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("H", "i"),
        );
        assert_cursor(
            EditMode::Vi,
            ("", "Hi"),
            &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter],
            ("Hi", ""),
        );
    }
}

#[test]
fn j() {
    for key in &[
        KeyPress::Char('j'),
        KeyPress::Char('+'),
        KeyPress::Ctrl('N'),
    ] {
        assert_history(
            EditMode::Vi,
            &["line1", "line2"],
            &[
                KeyPress::Esc,
                KeyPress::Ctrl('P'),
                KeyPress::Ctrl('P'),
                *key,
                KeyPress::Enter,
            ],
            ("line2", ""),
        );
    }
}

#[test]
fn k() {
    for key in &[
        KeyPress::Char('k'),
        KeyPress::Char('-'),
        KeyPress::Ctrl('P'),
    ] {
        assert_history(
            EditMode::Vi,
            &["line1"],
            &[KeyPress::Esc, *key, KeyPress::Enter],
            ("line1", ""),
        );
    }
}

#[test]
fn p() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('p'),
            KeyPress::Enter,
        ],
        (" Hello", ",world"),
    );
}

#[test]
fn uppercase_p() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[
            KeyPress::Esc,
            KeyPress::Ctrl('W'),
            KeyPress::Char('P'),
            KeyPress::Enter,
        ],
        ("Hello", ", world"),
    );
}

#[test]
fn r() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ", world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('r'),
            KeyPress::Char('o'),
            KeyPress::Enter,
        ],
        ("H", "o, world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("He", "llo, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('4'),
            KeyPress::Char('r'),
            KeyPress::Char('i'),
            KeyPress::Enter,
        ],
        ("Hiii", "i, world!"),
    );
}

#[test]
fn s() {
    assert_cursor(
        EditMode::Vi,
        ("Hi", ", world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('s'),
            KeyPress::Char('o'),
            KeyPress::Enter,
        ],
        ("Ho", ", world!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("He", "llo, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('4'),
            KeyPress::Char('s'),
            KeyPress::Char('i'),
            KeyPress::Enter,
        ],
        ("Hi", ", world!"),
    );
}

#[test]
fn uppercase_s() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, ", "world"),
        &[KeyPress::Esc, KeyPress::Char('S'), KeyPress::Enter],
        ("", ""),
    );
}

#[test]
fn t() {
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('t'),
            KeyPress::Char('r'),
            KeyPress::Enter,
        ],
        ("Hello, w", "orld!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("", "Hello, world!"),
        &[
            KeyPress::Esc,
            KeyPress::Char('3'),
            KeyPress::Char('t'),
            KeyPress::Char('l'),
            KeyPress::Enter,
        ],
        ("Hello, wo", "rld!"),
    );
}

#[test]
fn uppercase_t() {
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('T'),
            KeyPress::Char('r'),
            KeyPress::Enter,
        ],
        ("Hello, wor", "ld!"),
    );
    assert_cursor(
        EditMode::Vi,
        ("Hello, world!", ""),
        &[
            KeyPress::Esc,
            KeyPress::Char('3'),
            KeyPress::Char('T'),
            KeyPress::Char('l'),
            KeyPress::Enter,
        ],
        ("Hel", "lo, world!"),
    );
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 2 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//
//  in the shootout, they use 16000 as image size
//
//  writes pbm image to output path

extern mod std;
use io::WriterUtil;
use std::oldmap::HashMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx) -> bool
{
    let mut z = cmplx {re: 0f64, im: 0f64};
    let mut i = 0;
    let mut in = true;
    while i < 50 {
        z = z*z + x;
        if cabs(z) >= 4f64 {
            in = false;
            break;
        }
        i += 1;
    }
    in
}

fn fillbyte(x: cmplx, incr: f64) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint) -> Line
{
    let mut crv = ~[];
    let incr = 2f64/(size as f64);
    let y = incr*(i as f64) - 1f64;
    let xincr = 8f64*incr;
    for uint::range(0_u, size/8_u) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5f64, im: y};
        crv.push(fillbyte(x, incr));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line(~"P4");
    cout.write_line(fmt!("%u %u", size, size));
    let lines: HashMap<uint, ~[u8]> = HashMap();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", aline.i);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                if lines.contains_key(&prev) {
                    debug!("WS %u", prev);
                    cout.write(lines.get(&prev));
                    done += 1_u;
                    lines.remove(&prev);
                    prev += 1_u;
                }
                else {
                    break
                }
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, copy aline.b);    // FIXME: bad for perf
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000"]
    } else {
        args
    };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn || { cchan.send(chanmb(j, size)) };
    };
    writer(path, pport, size);
}
<commit_msg>auto merge of #5006 : tedhorst/rust/linear_mand, r=brson<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//  based on:
//  http://shootout.alioth.debian.org/
//   u64q/program.php?test=mandelbrot&lang=python3&id=2
//
//  takes 3 optional args:
//   square image size, defaults to 80_u
//   output path, default is "" (no output), "-" means stdout
//   depth (max iterations per pixel), defaults to 50_u
//
//  in the shootout, they use 16000 as image size, 50 as depth,
//   and write to stdout:
//
//  ./shootout_mandelbrot 16000 "-" 50 > /tmp/mandel.pbm
//
//  writes pbm image to output path

use io::WriterUtil;
use core::hashmap::linear::LinearMap;

struct cmplx {
    re: f64,
    im: f64
}

impl ops::Mul<cmplx,cmplx> for cmplx {
    pure fn mul(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re*(*x).re - self.im*(*x).im,
            im: self.re*(*x).im + self.im*(*x).re
        }
    }
}

impl ops::Add<cmplx,cmplx> for cmplx {
    pure fn add(&self, x: &cmplx) -> cmplx {
        cmplx {
            re: self.re + (*x).re,
            im: self.im + (*x).im
        }
    }
}

struct Line {i: uint, b: ~[u8]}

pure fn cabs(x: cmplx) -> f64
{
    x.re*x.re + x.im*x.im
}

fn mb(x: cmplx, depth: uint) -> bool
{
    let mut z = x;
    let mut i = 0;
    while i < depth {
        if cabs(z) >= 4_f64 {
            return false;
        }
        z = z*z + x;
        i += 1;
    }
    true
}

fn fillbyte(x: cmplx, incr: f64, depth: uint) -> u8 {
    let mut rv = 0_u8;
    let mut i = 0_u8;
    while i < 8_u8 {
        let z = cmplx {re: x.re + (i as f64)*incr, im: x.im};
        if mb(z, depth) {
            rv += 1_u8 << (7_u8 - i);
        }
        i += 1_u8;
    }
    rv
}

fn chanmb(i: uint, size: uint, depth: uint) -> Line
{
    let bsize = size/8_u;
    let mut crv = vec::with_capacity(bsize);
    let incr = 2_f64/(size as f64);
    let y = incr*(i as f64) - 1_f64;
    let xincr = 8_f64*incr;
    for uint::range(0_u, bsize) |j| {
        let x = cmplx {re: xincr*(j as f64) - 1.5_f64, im: y};
        crv.push(fillbyte(x, incr, depth));
    };
    Line {i:i, b:crv}
}

struct Devnull();

impl io::Writer for Devnull {
    fn write(&self, _b: &[const u8]) {}
    fn seek(&self, _i: int, _s: io::SeekStyle) {}
    fn tell(&self) -> uint {0_u}
    fn flush(&self) -> int {0}
    fn get_type(&self) -> io::WriterType { io::File }
}

fn writer(path: ~str, pport: pipes::Port<Line>, size: uint)
{
    let cout: io::Writer = match path {
        ~"" => {
            Devnull as io::Writer
        }
        ~"-" => {
            io::stdout()
        }
        _ => {
            result::get(
                &io::file_writer(&Path(path),
                ~[io::Create, io::Truncate]))
        }
    };
    cout.write_line("P4");
    cout.write_line(fmt!("%u %u", size, size));
    let mut lines: LinearMap<uint, Line> = LinearMap::new();
    let mut done = 0_u;
    let mut i = 0_u;
    while i < size {
        let aline = pport.recv();
        if aline.i == done {
            debug!("W %u", done);
            cout.write(aline.b);
            done += 1_u;
            let mut prev = done;
            while prev <= i {
                match lines.pop(&prev) {
                    Some(pl) => {
                        debug!("WS %u", prev);
                        cout.write(pl.b);
                        done += 1_u;
                        prev += 1_u;
                    }
                    None => break
                };
            };
        }
        else {
            debug!("S %u", aline.i);
            lines.insert(aline.i, aline);
        };
        i += 1_u;
    }
}

fn main() {
    let args = os::args();
    let args = if os::getenv(~"RUST_BENCH").is_some() {
        ~[~"", ~"4000", ~"50"]
    } else {
        args
    };

    let depth = if vec::len(args) < 4_u { 50_u }
    else { uint::from_str(args[3]).get() };

    let path = if vec::len(args) < 3_u { ~"" }
    else { copy args[2] };  // FIXME: bad for perf

    let size = if vec::len(args) < 2_u { 80_u }
    else { uint::from_str(args[1]).get() };

    let (pport, pchan) = pipes::stream();
    let pchan = pipes::SharedChan(pchan);
    for uint::range(0_u, size) |j| {
        let cchan = pchan.clone();
        do task::spawn { cchan.send(chanmb(j, size, depth)) };
    };
    writer(path, pport, size);
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{Write,Result};
use std::path::Path;

use build_scalar_array::{types};

fn declare_angle(f: &mut File) -> Result<()> {
    let un_ops = ["Neg"];
    let bin_ops = ["Add", "Div", "Rem", "Sub", "Mul"];

    try!(write!(f,"use std::ops::{{"));
    for trait_name in un_ops.iter().chain(bin_ops.iter()) {
        try!(write!(f,"{},", trait_name));
    }

    try!(write!(f,"}};\n"));
    for &(name, full_name, turn_prefix, turn_suffix) in [("Rad", "radians", "", "::consts::PI"), ("Deg", "degrees", "360", "")].iter() {
        for size in [32, 64].iter() {
            try!(write!(f,"

/// A {size}-bit floating point angle in {full_name}
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
pub struct {name}{size}(pub f{size});

impl Angle for {name}{size} {{
    type Type = f{size};

    // TODO: Convert to associated constant. Blocked by rust-lang/rust#30396
    //const TURN: Self::Type = {turn_prefix}f{size}{turn_suffix};
    // The numerical value representing full turn
    #[inline] fn turn() -> Self::Type {{ {turn_prefix}f{size}{turn_suffix} }}

    /// Computes the sine of a number.
    #[inline] fn sin(self) -> Self::Type {{ Rad{size}::from(self).0.sin() }}
    /// Computes the cosine of a number
    #[inline] fn cos(self) -> Self::Type {{ Rad{size}::from(self).0.cos() }}
    /// Computes the tangent of a number
    #[inline] fn tan(self) -> Self::Type {{ Rad{size}::from(self).0.tan() }}
    /// Simultaneously computes the sine and cosine of the number, `x`. Returns `(sin(x), cos(x))`.
    #[inline] fn sin_cos(self) -> (Self::Type, Self::Type) {{ Rad{size}::from(self).0.sin_cos() }}

    /// Computes the arcsine of a number. Return value is in the range [-TURN/4, TURN/4] or NaN if
    /// the number is outside the range [-1, 1].
    #[inline] fn asin(s: Self::Type) -> Self {{ s.asin().into() }}
    /// Computes the arccosine of a number. Return value is in the range [0, TURN/2] or NaN if the
    /// number is outside the range [-1, 1].
    #[inline] fn acos(s: Self::Type) -> Self {{ s.acos().into() }}
    /// Computes the arctangent of a number. Return value is in the range [-TURN/4, TURN/4];
    #[inline] fn atan(s: Self::Type) -> Self {{ s.atan().into() }}
    /// Computes the four quadrant arctangent of `y` and `x`.
    #[inline] fn atan2(y: Self::Type, x: Self::Type) -> Self {{ y.atan2(x).into() }}
}}

impl AsRef<f{size}> for {name}{size} {{ fn as_ref(&self) -> &f{size} {{ &self.0 }} }}
impl AsMut<f{size}> for {name}{size} {{ fn as_mut(&mut self) -> &mut f{size} {{ &mut self.0 }} }}
\n", full_name=full_name, name=name, size=size, turn_prefix=turn_prefix, turn_suffix=turn_suffix));
            for t in types().iter().filter(|t| **t != "bool") {
                try!(write!(f,"
impl From<{t:5}> for {name}{size} {{  fn from(v: {t:5}) -> Self {{ {name}{size}(v as f{size}) }}  }}
impl From<{name}{size}> for {t:5} {{  fn from(v: {name}{size}) -> Self {{ v.0 as {t} }}  }}\n", name=name, size=size, t=t));
            }

            try!(impl_unop(f, "Neg", name, *size));
            try!(impl_binop(f, "Add", name, name, *size));
            try!(impl_binop(f, "Sub", name, name, *size));
            try!(impl_binop(f, "Div", name, "f", *size));
            try!(impl_binop(f, "Rem", name, name, *size));
            try!(impl_binop_scalar(f, "Mul", name, name, *size));
        }
    }

    write!(f, "\n")
}

fn impl_unop(f: &mut File, trait_name: &str, name: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name} for {name}{size} {{
    type Output = {name}{size};
    fn {method_name}(self) -> Self::Output {{ {name}{size}({trait_name}::{method_name}(self.0)) }}
}}
impl <'t> {trait_name} for &'t {name}{size} {{
    type Output = {name}{size};
    fn {method_name}(self) -> Self::Output {{ {name}{size}({trait_name}::{method_name}(&self.0)) }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, size=size)
}

fn impl_binop(f: &mut File, trait_name: &str, name: &str, output: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name} for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: {name}{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(self.0, rhs.0).into() }}
}}
impl <'t> {trait_name}<{name}{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: {name}{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(&self.0, rhs.0).into() }}
}}
impl <'r> {trait_name}<&'r {name}{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r {name}{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(self.0, &rhs.0).into() }}
}}
impl <'t, 'r> {trait_name}<&'r {name}{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r {name}{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(&self.0, &rhs.0).into() }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, output=output, size=size)
}

fn impl_binop_scalar(f: &mut File, trait_name: &str, name: &str, output: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name}<f{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: f{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(self.0, rhs).into() }}
}}
impl <'t> {trait_name}<f{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: f{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(&self.0, rhs).into() }}
}}
impl <'r> {trait_name}<&'r f{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r f{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(self.0, rhs).into() }}
}}
impl <'t, 'r> {trait_name}<&'r f{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r f{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(&self.0, rhs).into() }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, output=output, size=size)
}

pub fn main(out_dir: &String) {
    let dest_path = Path::new(out_dir).join("angle.rs");
    let mut f = File::create(&dest_path).unwrap();
    declare_angle(&mut f).unwrap();
}
<commit_msg>Added travis.yml Removed pub from Angles<commit_after>use std::fs::File;
use std::io::{Write,Result};
use std::path::Path;

use build_scalar_array::{types};

fn declare_angle(f: &mut File) -> Result<()> {
    let un_ops = ["Neg"];
    let bin_ops = ["Add", "Div", "Rem", "Sub", "Mul"];

    try!(write!(f,"use std::ops::{{"));
    for trait_name in un_ops.iter().chain(bin_ops.iter()) {
        try!(write!(f,"{},", trait_name));
    }

    try!(write!(f,"}};\n"));
    for &(name, full_name, turn_prefix, turn_suffix) in [("Rad", "radians", "", "::consts::PI"), ("Deg", "degrees", "360", "")].iter() {
        for size in [32, 64].iter() {
            try!(write!(f,"

/// A {size}-bit floating point angle in {full_name}
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
pub struct {name}{size}(f{size});

impl Angle for {name}{size} {{
    type Type = f{size};

    // TODO: Convert to associated constant. Blocked by rust-lang/rust#30396
    //const TURN: Self::Type = {turn_prefix}f{size}{turn_suffix};
    // The numerical value representing full turn
    #[inline] fn turn() -> Self::Type {{ {turn_prefix}f{size}{turn_suffix} }}

    /// Computes the sine of a number.
    #[inline] fn sin(self) -> Self::Type {{ Rad{size}::from(self).0.sin() }}
    /// Computes the cosine of a number
    #[inline] fn cos(self) -> Self::Type {{ Rad{size}::from(self).0.cos() }}
    /// Computes the tangent of a number
    #[inline] fn tan(self) -> Self::Type {{ Rad{size}::from(self).0.tan() }}
    /// Simultaneously computes the sine and cosine of the number, `x`. Returns `(sin(x), cos(x))`.
    #[inline] fn sin_cos(self) -> (Self::Type, Self::Type) {{ Rad{size}::from(self).0.sin_cos() }}

    /// Computes the arcsine of a number. Return value is in the range [-TURN/4, TURN/4] or NaN if
    /// the number is outside the range [-1, 1].
    #[inline] fn asin(s: Self::Type) -> Self {{ s.asin().into() }}
    /// Computes the arccosine of a number. Return value is in the range [0, TURN/2] or NaN if the
    /// number is outside the range [-1, 1].
    #[inline] fn acos(s: Self::Type) -> Self {{ s.acos().into() }}
    /// Computes the arctangent of a number. Return value is in the range [-TURN/4, TURN/4];
    #[inline] fn atan(s: Self::Type) -> Self {{ s.atan().into() }}
    /// Computes the four quadrant arctangent of `y` and `x`.
    #[inline] fn atan2(y: Self::Type, x: Self::Type) -> Self {{ y.atan2(x).into() }}
}}

impl AsRef<f{size}> for {name}{size} {{ fn as_ref(&self) -> &f{size} {{ &self.0 }} }}
impl AsMut<f{size}> for {name}{size} {{ fn as_mut(&mut self) -> &mut f{size} {{ &mut self.0 }} }}
\n", full_name=full_name, name=name, size=size, turn_prefix=turn_prefix, turn_suffix=turn_suffix));
            for t in types().iter().filter(|t| **t != "bool") {
                try!(write!(f,"
impl From<{t:5}> for {name}{size} {{  fn from(v: {t:5}) -> Self {{ {name}{size}(v as f{size}) }}  }}
impl From<{name}{size}> for {t:5} {{  fn from(v: {name}{size}) -> Self {{ v.0 as {t} }}  }}\n", name=name, size=size, t=t));
            }

            try!(impl_unop(f, "Neg", name, *size));
            try!(impl_binop(f, "Add", name, name, *size));
            try!(impl_binop(f, "Sub", name, name, *size));
            try!(impl_binop(f, "Div", name, "f", *size));
            try!(impl_binop(f, "Rem", name, name, *size));
            try!(impl_binop_scalar(f, "Mul", name, name, *size));
        }
    }

    write!(f, "\n")
}

fn impl_unop(f: &mut File, trait_name: &str, name: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name} for {name}{size} {{
    type Output = {name}{size};
    fn {method_name}(self) -> Self::Output {{ {name}{size}({trait_name}::{method_name}(self.0)) }}
}}
impl <'t> {trait_name} for &'t {name}{size} {{
    type Output = {name}{size};
    fn {method_name}(self) -> Self::Output {{ {name}{size}({trait_name}::{method_name}(&self.0)) }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, size=size)
}

fn impl_binop(f: &mut File, trait_name: &str, name: &str, output: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name} for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: {name}{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(self.0, rhs.0).into() }}
}}
impl <'t> {trait_name}<{name}{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: {name}{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(&self.0, rhs.0).into() }}
}}
impl <'r> {trait_name}<&'r {name}{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r {name}{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(self.0, &rhs.0).into() }}
}}
impl <'t, 'r> {trait_name}<&'r {name}{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r {name}{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(&self.0, &rhs.0).into() }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, output=output, size=size)
}

fn impl_binop_scalar(f: &mut File, trait_name: &str, name: &str, output: &str, size: usize) -> Result<()> {
    write!(f, "
impl {trait_name}<f{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: f{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(self.0, rhs).into() }}
}}
impl <'t> {trait_name}<f{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: f{size}) -> Self::Output {{ {trait_name}::<f{size}>::{method_name}(&self.0, rhs).into() }}
}}
impl <'r> {trait_name}<&'r f{size}> for {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r f{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(self.0, rhs).into() }}
}}
impl <'t, 'r> {trait_name}<&'r f{size}> for &'t {name}{size} {{
    type Output = {output}{size};
    fn {method_name}(self, rhs: &'r f{size}) -> Self::Output {{ {trait_name}::<&'r f{size}>::{method_name}(&self.0, rhs).into() }}
}}\n", trait_name=trait_name, method_name=trait_name.to_lowercase(), name=name, output=output, size=size)
}

pub fn main(out_dir: &String) {
    let dest_path = Path::new(out_dir).join("angle.rs");
    let mut f = File::create(&dest_path).unwrap();
    declare_angle(&mut f).unwrap();
}
<|endoftext|>


<commit_before>use std::cmp;

use itertools;
use liquid_core::Expression;
use liquid_core::Result;
use liquid_core::Runtime;
use liquid_core::{
    Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter,
};
use liquid_core::{Value, ValueView};
use unicode_segmentation::UnicodeSegmentation;

#[derive(Debug, FilterParameters)]
struct TruncateArgs {
    #[parameter(
        description = "The maximum lenght of the string, after which it will be truncated.",
        arg_type = "integer"
    )]
    lenght: Option<Expression>,

    #[parameter(
        description = "The text appended to the end of the string if it is truncated. This text counts to the maximum lenght of the string. Defaults to \"...\".",
        arg_type = "str"
    )]
    ellipsis: Option<Expression>,
}

/// `truncate` shortens a string down to the number of characters passed as a parameter.
///
/// Note that this function operates on [grapheme
/// clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (or *user-perceived
/// character*), rather than Unicode code points.  Each grapheme cluster may be composed of more
/// than one Unicode code point, and does not necessarily correspond to rust's conception of a
/// character.
///
/// If the number of characters specified is less than the length of the string, an ellipsis
/// (`...`) is appended to the string and is included in the character count.
///
/// ## Custom ellipsis
///
/// `truncate` takes an optional second parameter that specifies the sequence of characters to be
/// appended to the truncated string. By default this is an ellipsis (`...`), but you can specify a
/// different sequence.
///
/// The length of the second parameter counts against the number of characters specified by the
/// first parameter. For example, if you want to truncate a string to exactly 10 characters, and
/// use a 3-character ellipsis, use 13 for the first parameter of `truncate`, since the ellipsis
/// counts as 3 characters.
///
/// ## No ellipsis
///
/// You can truncate to the exact number of characters specified by the first parameter and show no
/// trailing characters by passing a blank string as the second parameter.
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "truncate",
    description = "Shortens a string down to the number of characters passed as a parameter.",
    parameters(TruncateArgs),
    parsed(TruncateFilter)
)]
pub struct Truncate;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "truncate"]
struct TruncateFilter {
    #[parameters]
    args: TruncateArgs,
}

impl Filter for TruncateFilter {
    fn evaluate(&self, input: &dyn ValueView, runtime: &Runtime<'_>) -> Result<Value> {
        let args = self.args.evaluate(runtime)?;

        let lenght = args.lenght.unwrap_or(50) as usize;
        let truncate_string = args.ellipsis.unwrap_or_else(|| "...".into());
        let diff = if lenght >= truncate_string.len() {
            lenght - truncate_string.len()
        } else {
            0
        };
        let l = cmp::max(diff, 0);

        let input_string = input.to_kstr();
        let result = if lenght < input_string.len() {
            let result = UnicodeSegmentation::graphemes(input_string.as_str(), true)
                .take(l)
                .collect::<Vec<&str>>()
                .join("")
                + truncate_string.as_str();
            Value::scalar(result)
        } else {
            input.to_value()
        };
        Ok(result)
    }
}

#[derive(Debug, FilterParameters)]
struct TruncateWordsArgs {
    #[parameter(
        description = "The maximum number of words, after which the string will be truncated.",
        arg_type = "integer"
    )]
    lenght: Option<Expression>,

    #[parameter(
        description = "The text appended to the end of the string if it is truncated. This text counts to the maximum word-count of the string. Defaults to \"...\".",
        arg_type = "str"
    )]
    ellipsis: Option<Expression>,
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "truncatewords",
    description = "Shortens a string down to the number of characters passed as a parameter.",
    parameters(TruncateWordsArgs),
    parsed(TruncateWordsFilter)
)]
pub struct TruncateWords;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "truncate"]
struct TruncateWordsFilter {
    #[parameters]
    args: TruncateWordsArgs,
}

impl Filter for TruncateWordsFilter {
    fn evaluate(&self, input: &dyn ValueView, runtime: &Runtime<'_>) -> Result<Value> {
        let args = self.args.evaluate(runtime)?;

        let words = args.lenght.unwrap_or(50) as usize;

        let truncate_string = args.ellipsis.unwrap_or_else(|| "...".into());

        let l = cmp::max(words, 0);

        let input_string = input.to_kstr();

        let word_list: Vec<&str> = input_string.split(' ').collect();
        let result = if words < word_list.len() {
            let result = itertools::join(word_list.iter().take(l), " ") + truncate_string.as_str();
            Value::scalar(result)
        } else {
            input.to_value()
        };
        Ok(result)
    }
}

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

    #[test]
    fn unit_truncate() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation.",
                17i32
            )
            .unwrap(),
            liquid_core::value!("I often quote ...")
        );
    }

    #[test]
    fn unit_truncate_negative_length() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation.",
                -17i32
            )
            .unwrap(),
            liquid_core::value!("I often quote myself.  It adds spice to my conversation.")
        );
    }

    #[test]
    fn unit_truncate_non_string() {
        assert_eq!(
            liquid_core::call_filter!(Truncate, 10000000f64, 5i32).unwrap(),
            liquid_core::value!("10...")
        );
    }

    #[test]
    fn unit_truncate_shopify_liquid() {
        // Tests from https://shopify.github.io/liquid/filters/truncate/
        let input = "Ground control to Major Tom.";

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 20i32).unwrap(),
            liquid_core::value!("Ground control to...")
        );

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 25i32, ", and so on").unwrap(),
            liquid_core::value!("Ground control, and so on")
        );

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 20i32, "").unwrap(),
            liquid_core::value!("Ground control to Ma")
        );
    }

    #[test]
    fn unit_truncate_three_arguments() {
        liquid_core::call_filter!(
            Truncate,
            "I often quote myself.  It adds spice to my conversation.",
            17i32,
            "...",
            0i32
        )
        .unwrap_err();
    }

    #[test]
    fn unit_truncate_unicode_codepoints_examples() {
        // The examples below came from the unicode_segmentation documentation.
        //
        // https://unicode-rs.github.io/unicode-segmentation/unicode_segmentation/ ...
        //               ...  trait.UnicodeSegmentation.html#tymethod.graphemes
        //
        // Note that the accents applied to each letter are treated as part of the single grapheme
        // cluster for the applicable letter.
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "Here is an a\u{310}, e\u{301}, and o\u{308}\u{332}.",
                20i32
            )
            .unwrap(),
            liquid_core::value!("Here is an a\u{310}, e\u{301}, ...")
        );

        // Note that the 🇷🇺🇸🇹 is treated as a single grapheme cluster.
        assert_eq!(
            liquid_core::call_filter!(Truncate, "Here is a RUST: 🇷🇺🇸🇹.", 20i32).unwrap(),
            liquid_core::value!("Here is a RUST: 🇷🇺...")
        );
    }

    #[test]
    fn unit_truncate_zero_arguments() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation."
            )
            .unwrap(),
            liquid_core::value!("I often quote myself.  It adds spice to my conv...")
        );
    }

    #[test]
    fn unit_truncatewords_negative_length() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", -1_i32).unwrap(),
            liquid_core::value!("one two three")
        );
    }

    #[test]
    fn unit_truncatewords_zero_length() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 0_i32).unwrap(),
            liquid_core::value!("...")
        );
    }

    #[test]
    fn unit_truncatewords_no_truncation() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 4_i32).unwrap(),
            liquid_core::value!("one two three")
        );
    }

    #[test]
    fn unit_truncatewords_truncate() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32).unwrap(),
            liquid_core::value!("one two...")
        );
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32, 1_i32).unwrap(),
            liquid_core::value!("one two1")
        );
    }

    #[test]
    fn unit_truncatewords_empty_string() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "", 1_i32).unwrap(),
            liquid_core::value!("")
        );
    }
}
<commit_msg>fix(typo): Spell length properly<commit_after>use std::cmp;

use itertools;
use liquid_core::Expression;
use liquid_core::Result;
use liquid_core::Runtime;
use liquid_core::{
    Display_filter, Filter, FilterParameters, FilterReflection, FromFilterParameters, ParseFilter,
};
use liquid_core::{Value, ValueView};
use unicode_segmentation::UnicodeSegmentation;

#[derive(Debug, FilterParameters)]
struct TruncateArgs {
    #[parameter(
        description = "The maximum lenght of the string, after which it will be truncated.",
        arg_type = "integer"
    )]
    lenght: Option<Expression>,

    #[parameter(
        description = "The text appended to the end of the string if it is truncated. This text counts to the maximum lenght of the string. Defaults to \"...\".",
        arg_type = "str"
    )]
    ellipsis: Option<Expression>,
}

/// `truncate` shortens a string down to the number of characters passed as a parameter.
///
/// Note that this function operates on [grapheme
/// clusters](http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (or *user-perceived
/// character*), rather than Unicode code points.  Each grapheme cluster may be composed of more
/// than one Unicode code point, and does not necessarily correspond to rust's conception of a
/// character.
///
/// If the number of characters specified is less than the length of the string, an ellipsis
/// (`...`) is appended to the string and is included in the character count.
///
/// ## Custom ellipsis
///
/// `truncate` takes an optional second parameter that specifies the sequence of characters to be
/// appended to the truncated string. By default this is an ellipsis (`...`), but you can specify a
/// different sequence.
///
/// The length of the second parameter counts against the number of characters specified by the
/// first parameter. For example, if you want to truncate a string to exactly 10 characters, and
/// use a 3-character ellipsis, use 13 for the first parameter of `truncate`, since the ellipsis
/// counts as 3 characters.
///
/// ## No ellipsis
///
/// You can truncate to the exact number of characters specified by the first parameter and show no
/// trailing characters by passing a blank string as the second parameter.
#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "truncate",
    description = "Shortens a string down to the number of characters passed as a parameter.",
    parameters(TruncateArgs),
    parsed(TruncateFilter)
)]
pub struct Truncate;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "truncate"]
struct TruncateFilter {
    #[parameters]
    args: TruncateArgs,
}

impl Filter for TruncateFilter {
    fn evaluate(&self, input: &dyn ValueView, runtime: &Runtime<'_>) -> Result<Value> {
        let args = self.args.evaluate(runtime)?;

        let length = args.lenght.unwrap_or(50) as usize;
        let truncate_string = args.ellipsis.unwrap_or_else(|| "...".into());
        let diff = if length >= truncate_string.len() {
            length - truncate_string.len()
        } else {
            0
        };
        let l = cmp::max(diff, 0);

        let input_string = input.to_kstr();
        let result = if length < input_string.len() {
            let result = UnicodeSegmentation::graphemes(input_string.as_str(), true)
                .take(l)
                .collect::<Vec<&str>>()
                .join("")
                + truncate_string.as_str();
            Value::scalar(result)
        } else {
            input.to_value()
        };
        Ok(result)
    }
}

#[derive(Debug, FilterParameters)]
struct TruncateWordsArgs {
    #[parameter(
        description = "The maximum number of words, after which the string will be truncated.",
        arg_type = "integer"
    )]
    lenght: Option<Expression>,

    #[parameter(
        description = "The text appended to the end of the string if it is truncated. This text counts to the maximum word-count of the string. Defaults to \"...\".",
        arg_type = "str"
    )]
    ellipsis: Option<Expression>,
}

#[derive(Clone, ParseFilter, FilterReflection)]
#[filter(
    name = "truncatewords",
    description = "Shortens a string down to the number of characters passed as a parameter.",
    parameters(TruncateWordsArgs),
    parsed(TruncateWordsFilter)
)]
pub struct TruncateWords;

#[derive(Debug, FromFilterParameters, Display_filter)]
#[name = "truncate"]
struct TruncateWordsFilter {
    #[parameters]
    args: TruncateWordsArgs,
}

impl Filter for TruncateWordsFilter {
    fn evaluate(&self, input: &dyn ValueView, runtime: &Runtime<'_>) -> Result<Value> {
        let args = self.args.evaluate(runtime)?;

        let words = args.lenght.unwrap_or(50) as usize;

        let truncate_string = args.ellipsis.unwrap_or_else(|| "...".into());

        let l = cmp::max(words, 0);

        let input_string = input.to_kstr();

        let word_list: Vec<&str> = input_string.split(' ').collect();
        let result = if words < word_list.len() {
            let result = itertools::join(word_list.iter().take(l), " ") + truncate_string.as_str();
            Value::scalar(result)
        } else {
            input.to_value()
        };
        Ok(result)
    }
}

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

    #[test]
    fn unit_truncate() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation.",
                17i32
            )
            .unwrap(),
            liquid_core::value!("I often quote ...")
        );
    }

    #[test]
    fn unit_truncate_negative_length() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation.",
                -17i32
            )
            .unwrap(),
            liquid_core::value!("I often quote myself.  It adds spice to my conversation.")
        );
    }

    #[test]
    fn unit_truncate_non_string() {
        assert_eq!(
            liquid_core::call_filter!(Truncate, 10000000f64, 5i32).unwrap(),
            liquid_core::value!("10...")
        );
    }

    #[test]
    fn unit_truncate_shopify_liquid() {
        // Tests from https://shopify.github.io/liquid/filters/truncate/
        let input = "Ground control to Major Tom.";

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 20i32).unwrap(),
            liquid_core::value!("Ground control to...")
        );

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 25i32, ", and so on").unwrap(),
            liquid_core::value!("Ground control, and so on")
        );

        assert_eq!(
            liquid_core::call_filter!(Truncate, input, 20i32, "").unwrap(),
            liquid_core::value!("Ground control to Ma")
        );
    }

    #[test]
    fn unit_truncate_three_arguments() {
        liquid_core::call_filter!(
            Truncate,
            "I often quote myself.  It adds spice to my conversation.",
            17i32,
            "...",
            0i32
        )
        .unwrap_err();
    }

    #[test]
    fn unit_truncate_unicode_codepoints_examples() {
        // The examples below came from the unicode_segmentation documentation.
        //
        // https://unicode-rs.github.io/unicode-segmentation/unicode_segmentation/ ...
        //               ...  trait.UnicodeSegmentation.html#tymethod.graphemes
        //
        // Note that the accents applied to each letter are treated as part of the single grapheme
        // cluster for the applicable letter.
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "Here is an a\u{310}, e\u{301}, and o\u{308}\u{332}.",
                20i32
            )
            .unwrap(),
            liquid_core::value!("Here is an a\u{310}, e\u{301}, ...")
        );

        // Note that the 🇷🇺🇸🇹 is treated as a single grapheme cluster.
        assert_eq!(
            liquid_core::call_filter!(Truncate, "Here is a RUST: 🇷🇺🇸🇹.", 20i32).unwrap(),
            liquid_core::value!("Here is a RUST: 🇷🇺...")
        );
    }

    #[test]
    fn unit_truncate_zero_arguments() {
        assert_eq!(
            liquid_core::call_filter!(
                Truncate,
                "I often quote myself.  It adds spice to my conversation."
            )
            .unwrap(),
            liquid_core::value!("I often quote myself.  It adds spice to my conv...")
        );
    }

    #[test]
    fn unit_truncatewords_negative_length() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", -1_i32).unwrap(),
            liquid_core::value!("one two three")
        );
    }

    #[test]
    fn unit_truncatewords_zero_length() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 0_i32).unwrap(),
            liquid_core::value!("...")
        );
    }

    #[test]
    fn unit_truncatewords_no_truncation() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 4_i32).unwrap(),
            liquid_core::value!("one two three")
        );
    }

    #[test]
    fn unit_truncatewords_truncate() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32).unwrap(),
            liquid_core::value!("one two...")
        );
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "one two three", 2_i32, 1_i32).unwrap(),
            liquid_core::value!("one two1")
        );
    }

    #[test]
    fn unit_truncatewords_empty_string() {
        assert_eq!(
            liquid_core::call_filter!(TruncateWords, "", 1_i32).unwrap(),
            liquid_core::value!("")
        );
    }
}
<|endoftext|>


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

use collections::enum_set::{EnumSet};
use libc::{c_char, c_int, c_uchar, c_void};
use std::ffi::{CString};
use std::ptr;
use std::result::{Result};
use std::string::{String};

mod native;

pub type compile_options = c_int;
pub type exec_options = c_int;
pub type fullinfo_field = c_int;
pub struct pcre;
pub type pcre_error = c_int;

pub type study_options = c_int;

pub type extra_options = c_int;

pub static PCRE_UTF8: c_int = 0x00000800;
pub static PCRE_NO_UTF8_CHECK: c_int = 0x00002000;

pub static PCRE_ERROR_NOMATCH: pcre_error = -1;
pub static PCRE_ERROR_NULL: pcre_error = -2;

pub static PCRE_INFO_CAPTURECOUNT: fullinfo_field = 2;
pub static PCRE_INFO_NAMEENTRYSIZE: fullinfo_field = 7;
pub static PCRE_INFO_NAMECOUNT: fullinfo_field = 8;
pub static PCRE_INFO_NAMETABLE: fullinfo_field = 9;

pub unsafe fn pcre_compile(pattern: *const c_char, options: &EnumSet<::CompileOption>, tableptr: *const c_uchar) -> Result<*mut pcre, (Option<String>, c_int)> {
    assert!(!pattern.is_null());
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as compile_options)) | PCRE_UTF8 | PCRE_NO_UTF8_CHECK;
    let mut err: *const c_char = ptr::null();
    let mut erroffset: c_int = 0;
    let code = native::pcre_compile(pattern, converted_options, &mut err, &mut erroffset, tableptr);

    if code.is_null() {
        // "Otherwise, if  compilation  of  a  pattern fails, pcre_compile() returns
        // NULL, and sets the variable pointed to by errptr to point to a textual
        // error message. This is a static string that is part of the library. You
        // must not try to free it."
        // http://pcre.org/pcre.txt
        let err_cstring = CString::new(err, false);

        match err_cstring.as_str() {
            None => Err((None, erroffset)),
            Some(err_str) => Err((Some(err_str.to_owned()), erroffset))
        }
    } else {
        assert!(!code.is_null());
        assert_eq!(erroffset, 0);

        Ok(code)
    }
}

pub unsafe fn pcre_exec(code: *const pcre, extra: *const ::PcreExtra, subject: *const c_char, length: c_int, startoffset: c_int, options: &EnumSet<::ExecOption>, ovector: *mut c_int, ovecsize: c_int) -> c_int {
    assert!(!code.is_null());
    assert!(ovecsize >= 0 && ovecsize % 3 == 0);
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as compile_options)) | PCRE_NO_UTF8_CHECK;
    let rc = native::pcre_exec(code, extra, subject, length, startoffset, converted_options, ovector, ovecsize);
    if rc == PCRE_ERROR_NOMATCH {
        return -1;
    } else if rc < 0 && rc != PCRE_ERROR_NULL {
        panic!("pcre_exec");
    }

    rc
}

pub unsafe fn pcre_free(ptr: *mut c_void) {
    native::pcre_free(ptr);
}

pub unsafe fn pcre_free_study(extra: *mut ::PcreExtra) {
    native::pcre_free_study(extra);
}

pub unsafe fn pcre_fullinfo(code: *const pcre, extra: *const ::PcreExtra, what: fullinfo_field, where_: *mut c_void) {
    assert!(!code.is_null());
    let rc = native::pcre_fullinfo(code, extra, what, where_);
    if rc < 0 && rc != PCRE_ERROR_NULL {
        panic!("pcre_fullinfo");
    }
}

pub unsafe fn pcre_refcount(code: *mut ::detail::pcre, adjust: c_int) -> c_int {
    assert!(!code.is_null());
    let curr_refcount = native::pcre_refcount(code, 0);
    if curr_refcount + adjust < 0 {
        panic!("refcount underflow");
    } else if curr_refcount + adjust > 65535 {
        panic!("refcount overflow");
    }
    native::pcre_refcount(code, adjust)
}

pub unsafe fn pcre_study(code: *const ::detail::pcre, options: &EnumSet<::StudyOption>) -> *mut ::PcreExtra {
    assert!(!code.is_null());
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as study_options));
    let mut err: *const c_char = ptr::null();
    let extra = native::pcre_study(code, converted_options, &mut err);
    // "The third argument for pcre_study() is a pointer for an error message. If
    // studying succeeds (even if no data is returned), the variable it points to is
    // set to NULL. Otherwise it is set to point to a textual error message. This is
    // a static string that is part of the library. You must not try to free it."
    // http://pcre.org/pcre.txt
    if !err.is_null() {
        let err_cstring = CString::new(err, false);
        match err_cstring.as_str() {
            None          => error!("pcre_study() failed"),
            Some(err_str) => error!("pcre_study() failed: {}", err_str)
        }
        panic!("pcre_study");
    }
    assert!(err.is_null());

    extra
}

pub fn pcre_version() -> String {
    let version_cstring = unsafe { CString::new(native::pcre_version(), false) };
    version_cstring.as_str().unwrap().to_owned()
}
<commit_msg>Convert PCRE's error C string to a `String` in a different way<commit_after>// Copyright 2015 The rust-pcre authors.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use collections::enum_set::{EnumSet};
use libc::{c_char, c_int, c_uchar, c_void};
use std::ffi::{CStr};
use std::ptr;
use std::result::{Result};
use std::string::{String};

mod native;

pub type compile_options = c_int;
pub type exec_options = c_int;
pub type fullinfo_field = c_int;
pub struct pcre;
pub type pcre_error = c_int;

pub type study_options = c_int;

pub type extra_options = c_int;

pub static PCRE_UTF8: c_int = 0x00000800;
pub static PCRE_NO_UTF8_CHECK: c_int = 0x00002000;

pub static PCRE_ERROR_NOMATCH: pcre_error = -1;
pub static PCRE_ERROR_NULL: pcre_error = -2;

pub static PCRE_INFO_CAPTURECOUNT: fullinfo_field = 2;
pub static PCRE_INFO_NAMEENTRYSIZE: fullinfo_field = 7;
pub static PCRE_INFO_NAMECOUNT: fullinfo_field = 8;
pub static PCRE_INFO_NAMETABLE: fullinfo_field = 9;

pub unsafe fn pcre_compile(pattern: *const c_char, options: &EnumSet<::CompileOption>, tableptr: *const c_uchar) -> Result<*mut pcre, (Option<String>, c_int)> {
    assert!(!pattern.is_null());
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as compile_options)) | PCRE_UTF8 | PCRE_NO_UTF8_CHECK;
    let mut err: *const c_char = ptr::null();
    let mut erroffset: c_int = 0;
    let code = native::pcre_compile(pattern, converted_options, &mut err, &mut erroffset, tableptr);

    if code.is_null() {
        // "Otherwise, if  compilation  of  a  pattern fails, pcre_compile() returns
        // NULL, and sets the variable pointed to by errptr to point to a textual
        // error message. This is a static string that is part of the library. You
        // must not try to free it."
        // http://pcre.org/pcre.txt
        let err_cstr = CStr::from_ptr(err);
        // http://illegalargumentexception.blogspot.com/2015/05/rust-utf-8-byte-array-to-string.html
        // TODO Investigate memory allocations and check for alternative solutions.
        match String::from_utf8(Vec::from(err_cstr.to_bytes())) {
            Err(_) => Err((None, erroffset)),
            Ok(err_str) => Err((Some(err_str), erroffset))
        }
    } else {
        assert!(!code.is_null());
        assert_eq!(erroffset, 0);

        Ok(code)
    }
}

pub unsafe fn pcre_exec(code: *const pcre, extra: *const ::PcreExtra, subject: *const c_char, length: c_int, startoffset: c_int, options: &EnumSet<::ExecOption>, ovector: *mut c_int, ovecsize: c_int) -> c_int {
    assert!(!code.is_null());
    assert!(ovecsize >= 0 && ovecsize % 3 == 0);
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as compile_options)) | PCRE_NO_UTF8_CHECK;
    let rc = native::pcre_exec(code, extra, subject, length, startoffset, converted_options, ovector, ovecsize);
    if rc == PCRE_ERROR_NOMATCH {
        return -1;
    } else if rc < 0 && rc != PCRE_ERROR_NULL {
        panic!("pcre_exec");
    }

    rc
}

pub unsafe fn pcre_free(ptr: *mut c_void) {
    native::pcre_free(ptr);
}

pub unsafe fn pcre_free_study(extra: *mut ::PcreExtra) {
    native::pcre_free_study(extra);
}

pub unsafe fn pcre_fullinfo(code: *const pcre, extra: *const ::PcreExtra, what: fullinfo_field, where_: *mut c_void) {
    assert!(!code.is_null());
    let rc = native::pcre_fullinfo(code, extra, what, where_);
    if rc < 0 && rc != PCRE_ERROR_NULL {
        panic!("pcre_fullinfo");
    }
}

pub unsafe fn pcre_refcount(code: *mut ::detail::pcre, adjust: c_int) -> c_int {
    assert!(!code.is_null());
    let curr_refcount = native::pcre_refcount(code, 0);
    if curr_refcount + adjust < 0 {
        panic!("refcount underflow");
    } else if curr_refcount + adjust > 65535 {
        panic!("refcount overflow");
    }
    native::pcre_refcount(code, adjust)
}

pub unsafe fn pcre_study(code: *const ::detail::pcre, options: &EnumSet<::StudyOption>) -> *mut ::PcreExtra {
    assert!(!code.is_null());
    let converted_options = options.iter().fold(0, |converted_options, option| converted_options | (option as study_options));
    let mut err: *const c_char = ptr::null();
    let extra = native::pcre_study(code, converted_options, &mut err);
    // "The third argument for pcre_study() is a pointer for an error message. If
    // studying succeeds (even if no data is returned), the variable it points to is
    // set to NULL. Otherwise it is set to point to a textual error message. This is
    // a static string that is part of the library. You must not try to free it."
    // http://pcre.org/pcre.txt
    if !err.is_null() {
        let err_cstr = CStr::from_ptr(err);
        match String::from_utf8(Vec::from(err_cstr.to_bytes())) {
            Err(_) => error!("pcre_study() failed"),
            Ok(err_str) => error!("pcre_study() failed: {}", err_str)
        }
        panic!("pcre_study");
    }
    assert!(err.is_null());

    extra
}

pub fn pcre_version() -> String {
    let version_cstr = unsafe { CStr::from_ptr(native::pcre_version()) };
    String::from_utf8(Vec::from(version_cstr.to_bytes())).unwrap()
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(interpolate_idents)]

extern crate libc;

#[macro_use]
extern crate apache2;

use apache2::{Request, Status, Pool, CmdParms, BoolType, StringType, RSRC_CONF};

new_module!(
   conf_new, b"mod_conf_new\0",
   config {
      server {
         ExampleConfig {
            enabled: BoolType,
            string_var: StringType
         },
         create_example_server_config
      }, [
         (FLAG, b"EnabledVar\0", enabled_var, RSRC_CONF, b"Example flag\0"),
         (TAKE1, b"StringVar\0", string_var, RSRC_CONF, b"Example string string directive\0")
      ]
   }
);

fn create_example_server_config<'a>(pool: &mut Pool) -> ExampleConfig<'a> {
   let mut config = ExampleConfig::new(pool).unwrap();

   config.set_enabled(false);

   config
}

fn enabled_var(parms: &mut CmdParms, on: bool) -> Result<(), ()> {
   let mut config = get_module_config(
      &mut try!(parms.pool()),
      &try!(try!(parms.server()).module_config())
   );

   config.set_enabled(on);

   Ok(())
}

fn string_var<'a>(parms: &mut CmdParms, w: &'a str) -> Result<(), ()> {
   let mut config = get_module_config(
      &mut try!(parms.pool()),
      &try!(try!(parms.server()).module_config())
   );

   config.set_string_var(w);

   Ok(())
}

fn conf_new_handler(r: &mut Request) -> Result<Status, ()> {
   if try!(r.handler()) != "conf" {
      return Ok(Status::DECLINED)
   }

   let config = get_module_config(
      &mut try!(r.pool()),
      &try!(try!(r.server()).module_config())
   );

   r.set_content_type("text/plain; charset=utf-8");

   let enabled = try!(config.enabled());
   try!(r.write(format!("EnabledVar: {:?}\n", enabled)));

   let string_var = try!(config.string_var());
   try!(r.write(format!("StringVar: {:?}\n", string_var)));

   Ok(Status::OK)
}
<commit_msg>Renaming definitions in the conf module.<commit_after>#![feature(plugin)]
#![plugin(interpolate_idents)]

extern crate libc;

#[macro_use]
extern crate apache2;

use apache2::{Request, Status, Pool, CmdParms, BoolType, StringType, RSRC_CONF};

new_module!(
   conf, b"mod_conf\0",
   config {
      server {
         ExampleConfig {
            enabled: BoolType,
            string_var: StringType
         },
         create_example_server_config
      }, [
         (FLAG, b"EnabledVar\0", enabled_var, RSRC_CONF, b"Example flag\0"),
         (TAKE1, b"StringVar\0", string_var, RSRC_CONF, b"Example string string directive\0")
      ]
   }
);

fn create_example_server_config<'a>(pool: &mut Pool) -> ExampleConfig<'a> {
   let mut config = ExampleConfig::new(pool).unwrap();

   config.set_enabled(false);

   config
}

fn enabled_var(parms: &mut CmdParms, on: bool) -> Result<(), ()> {
   let mut config = get_module_config(
      &mut try!(parms.pool()),
      &try!(try!(parms.server()).module_config())
   );

   config.set_enabled(on);

   Ok(())
}

fn string_var<'a>(parms: &mut CmdParms, w: &'a str) -> Result<(), ()> {
   let mut config = get_module_config(
      &mut try!(parms.pool()),
      &try!(try!(parms.server()).module_config())
   );

   config.set_string_var(w);

   Ok(())
}

fn conf_handler(r: &mut Request) -> Result<Status, ()> {
   if try!(r.handler()) != "conf" {
      return Ok(Status::DECLINED)
   }

   let config = get_module_config(
      &mut try!(r.pool()),
      &try!(try!(r.server()).module_config())
   );

   r.set_content_type("text/plain; charset=utf-8");

   let enabled = try!(config.enabled());
   try!(r.write(format!("EnabledVar: {:?}\n", enabled)));

   let string_var = try!(config.string_var());
   try!(r.write(format!("StringVar: {:?}\n", string_var)));

   Ok(Status::OK)
}
<|endoftext|>


<commit_before>use std::fs::{File, OpenOptions, metadata};
use std::path::{Path, PathBuf};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encode_into, decode_from};

use super::Error;
use super::database::Database;
use super::column::Column;
use super::rows::Rows;

use super::super::identifier::Identifier;

pub struct Table<'a> {
    database: &'a Database,
    name: Identifier,
    meta_data: TableMetadata
}

#[derive(Clone, RustcDecodable, RustcEncodable)]
struct TableMetadata {
    engine_id: u8,
    columns: Vec<Column>
}

/// Table representation
impl<'a> Table<'a> {
    /// Create table in database
    pub fn create<'b>(name: &str, engine_id: u8, columns: Vec<Column>, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("creating table: {}", name);

        // TODO: verify if name is a valid identifier

        let metadata = TableMetadata {
            engine_id: engine_id,
            columns: columns
        };

        let mut buf = try!(File::create(db.get_path().clone().join(&name).with_extension("tbl")));

        encode_into(&metadata, &mut buf, SizeLimit::Infinite).unwrap();

        Ok(Table {
            database: &db,
            name: try!(Identifier::new(name)),
            meta_data: metadata.clone()
        })
    }

    /// Load table from database
    pub fn load<'b>(name: &str, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("loading table: {}", &name);
        let path = db.get_path().clone().join(&name).with_extension("tbl");
        if !try!(metadata(&path)).is_dir()
        {
            let mut file = try!(OpenOptions::new()
                        .read(true)
                        .open(&path));

            let metadata: TableMetadata = try!(decode_from(&mut file, SizeLimit::Infinite));
            info!("loaded table: {}", &name);

            Ok(Table {
                database: db,
                name: try!(Identifier::new(name)),
                meta_data: metadata
            })
        }else {
            error!("could not load table: {} at {}", &name, &path.to_str().unwrap());
            Err(Error::LoadTable)
        }
    }

    pub fn get_cols_sizes(&self) -> Vec<u32> {
        let mut column_sizes = Vec::<u32>::new();

        for v in &self.meta_data.columns {
            column_sizes.push(v.size());
        }

        column_sizes
    }

    pub fn get_table_header_offset(&self) -> u64 {
        let bytes: Vec<u8> = encode(&self.meta_data, SizeLimit::Infinite).unwrap();
        bytes.len() as u64
    }

    /// Get full file path including filename and ext
    pub fn get_path(&self) -> PathBuf {
        self.database.get_path().clone().join(&self.name.get_name()).with_extension("tbl")
    }

    pub fn get_engine_id(&self) -> u8 {
        self.meta_data.engine_id
    }

}

#[cfg(test)]
mod test {
    use super::Table;
    use super::super::database::*;
    use std::fs::metadata;
    use super::super::column::Column;
    use super::super::types::Types;

    #[test]
    fn create_table() {
        let path = "/tmp/test1/";
        let name = "test_db3";
        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 0, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());
    }

    #[test]
    fn load_table() {
        let path = "/tmp/test1/";
        let name = "test_db4";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());

        let table2 = Table::load("test_table1", &db).unwrap();

        assert_eq!(table.get_engine_id(), table2.get_engine_id());
        assert_eq!(&table.meta_data.columns, &table2.meta_data.columns);
    }

    #[test]
    fn table_offset() {
        let path = "/tmp/test1/";
        let name = "test_db5";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert_eq!(table.get_table_header_offset(), 42u64);

    }

}
<commit_msg>added table offset helper methods<commit_after>use std::fs::{File, OpenOptions, metadata};
use std::path::{Path, PathBuf};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, encode_into, decode_from};

use super::Error;
use super::database::Database;
use super::column::Column;
use super::rows::Rows;

use super::super::identifier::Identifier;

pub struct Table<'a> {
    database: &'a Database,
    name: Identifier,
    meta_data: TableMetadata
}

#[derive(Clone, RustcDecodable, RustcEncodable)]
struct TableMetadata {
    engine_id: u8,
    columns: Vec<Column>
}

/// Table representation
impl<'a> Table<'a> {
    /// Create table in database
    pub fn create<'b>(name: &str, engine_id: u8, columns: Vec<Column>, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("creating table: {}", name);

        // TODO: verify if name is a valid identifier

        let metadata = TableMetadata {
            engine_id: engine_id,
            columns: columns
        };

        let mut buf = try!(File::create(db.get_path().clone().join(&name).with_extension("tbl")));

        encode_into(&metadata, &mut buf, SizeLimit::Infinite).unwrap();

        Ok(Table {
            database: &db,
            name: try!(Identifier::new(name)),
            meta_data: metadata.clone()
        })
    }

    /// Load table from database
    pub fn load<'b>(name: &str, db: &'b Database) -> Result<Table<'b>, Error> {
        info!("loading table: {}", &name);
        let path = db.get_path().clone().join(&name).with_extension("tbl");
        if !try!(metadata(&path)).is_dir()
        {
            let mut file = try!(OpenOptions::new()
                        .read(true)
                        .open(&path));

            let metadata: TableMetadata = try!(decode_from(&mut file, SizeLimit::Infinite));
            info!("loaded table: {}", &name);

            Ok(Table {
                database: db,
                name: try!(Identifier::new(name)),
                meta_data: metadata
            })
        }else {
            error!("could not load table: {} at {}", &name, &path.to_str().unwrap());
            Err(Error::LoadTable)
        }
    }

    /// Returns a vector of the sizes of the columns
    pub fn get_cols_sizes(&self) -> Vec<u32> {
        let mut column_sizes = Vec::<u32>::new();

        for v in &self.meta_data.columns {
            column_sizes.push(v.size());
        }

        column_sizes
    }

    pub fn get_table_header_offset(&self) -> u32 {
        let bytes: Vec<u8> = encode(&self.meta_data, SizeLimit::Infinite).unwrap();
        bytes.len() as u32
    }

    pub fn get_cols_offset(&self) -> u32 {
        let mut size = 0;

        for v in &self.meta_data.columns {
            size += v.size();
        }

        size
    }

    /// Get full file path including filename and ext
    pub fn get_path(&self) -> PathBuf {
        self.database.get_path().clone().join(&self.name.get_name()).with_extension("tbl")
    }

    pub fn get_engine_id(&self) -> u8 {
        self.meta_data.engine_id
    }

}

#[cfg(test)]
mod test {
    use super::Table;
    use super::super::database::*;
    use std::fs::metadata;
    use super::super::column::Column;
    use super::super::types::Types;

    #[test]
    fn create_table() {
        let path = "/tmp/test1/";
        let name = "test_db3";
        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 0, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());
    }

    #[test]
    fn load_table() {
        let path = "/tmp/test1/";
        let name = "test_db4";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert!(!metadata("/tmp/test1/test_db3/test_table1.tbl").unwrap().is_dir());

        let table2 = Table::load("test_table1", &db).unwrap();

        assert_eq!(table.get_engine_id(), table2.get_engine_id());
        assert_eq!(&table.meta_data.columns, &table2.meta_data.columns);
    }

    #[test]
    fn table_offset() {
        let path = "/tmp/test1/";
        let name = "test_db5";

        let mut columns = Vec::<Column>::new();
        columns.push(Column::new("Name", Types::Char(10)));
        columns.push(Column::new("Age", Types::Int));

        let db = Database::create(&name, DatabaseConfig::new(&path)).unwrap();
        let table = Table::create("test_table1", 34, columns, &db).unwrap();

        assert_eq!(table.get_table_header_offset(), 42u64);

    }

}
<|endoftext|>


<commit_before>use Renderable;
use Block;
use Value;
use template::Template;
use LiquidOptions;
use tags::IfBlock;
use lexer::Token;
use lexer::Token::{Identifier, StringLiteral, NumberLiteral, Comparison};
use lexer::ComparisonOperator;
use lexer::ComparisonOperator::{
    Equals, NotEquals,
    LessThan, GreaterThan,
    LessThanEquals, GreaterThanEquals,
    Contains
};
use parser::parse;
use lexer::Element;
use lexer::Element::{Tag};
use std::collections::HashMap;

struct If<'a>{
    lh : Token,
    comparison : ComparisonOperator,
    rh : Token,
    if_true: Template<'a>,
    if_false: Option<Template<'a>>
}

impl<'a> If<'a>{
    fn compare(&self, context: &HashMap<String, Value>) -> Result<bool, &'static str>{
        match (&self.lh, &self.rh)  {
            (&NumberLiteral(a), &NumberLiteral(b)) => Ok(compare_numbers(a, b, &self.comparison)),
            (&Identifier(ref var), &NumberLiteral(b)) => {
                match context.get(var.as_slice()) {
                    Some(&Value::Num(a)) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            },
            (&NumberLiteral(a), &Identifier(ref var)) => {
                match context.get(var.as_slice()) {
                    Some(&Value::Num(b)) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            }
            (&Identifier(ref var_a), &Identifier(ref var_b)) => {
                match (context.get(var_a.as_slice()), context.get(var_b.as_slice())) {
                    (Some(&Value::Num(a)), Some(&Value::Num(b))) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            }
            (_, _) => Err("not implemented yet!") // TODO
        }
    }
}

fn compare_numbers(a : f32, b : f32, comparison : &ComparisonOperator) -> bool{
    match comparison {
        &Equals => a == b,
        &NotEquals => a != b,
        &LessThan => a < b,
        &GreaterThan => a > b,
        &LessThanEquals => a <= b,
        &GreaterThanEquals => a >= b,
        &Contains => false, // TODO!!!
    }
}

impl<'a> Renderable for If<'a>{
    fn render(&self, context: &HashMap<String, Value>) -> Option<String>{
        if self.compare(context).unwrap_or(false){
            self.if_true.render(context)
        }else{
            match self.if_false {
                Some(ref template) => template.render(context),
                _ => None
            }
        }
    }
}

impl<'a> Block for IfBlock<'a>{
    fn initialize<'a>(&'a self, tag_name: &str, arguments: &[Token], tokens: Vec<Element>, options : &'a LiquidOptions<'a>) -> Box<Renderable>{
        let mut args = arguments.iter();

        let lh = match args.next() {
            Some(&StringLiteral(ref x)) => StringLiteral(x.clone()),
            Some(&NumberLiteral(ref x)) => NumberLiteral(x.clone()),
            Some(&Identifier(ref x)) => Identifier(x.clone()),
            x => panic!("Expected a value, found {}", x)
        };

        let comp = match args.next() {
            Some(&Comparison(ref x)) => x.clone(),
            x => panic!("Expected a comparison operator, found {}", x)
        };

        let rh = match args.next() {
            Some(&StringLiteral(ref x)) => StringLiteral(x.clone()),
            Some(&NumberLiteral(ref x)) => NumberLiteral(x.clone()),
            Some(&Identifier(ref x)) => Identifier(x.clone()),
            x => panic!("Expected a value, found {}", x)
        };

        let else_block = vec![Identifier("else".to_string())];

        let if_true_tokens = tokens.iter().take_while(|&x| match x  {
            &Tag(ref eb, _) => *eb != else_block,
            _ => true
        }).map(|x| x.clone()).collect();

        let if_false_tokens : Vec<Element> = tokens.iter().skip_while(|&x| match x  {
            &Tag(ref eb, _) => *eb != else_block,
            _ => true
        }).map(|x| x.clone()).collect();

        let if_false = if if_false_tokens.len() > 0 {
            Some(Template::new(parse(if_false_tokens, options)))
        }else{
            None
        };

        box If{
            lh : lh,
            comparison : comp,
            rh : rh,
            if_true: Template::new(parse(if_true_tokens, options)),
            if_false: if_false
        } as Box<Renderable>
    }
}
<commit_msg>Add if block test<commit_after>use Renderable;
use Block;
use Value;
use template::Template;
use LiquidOptions;
use tags::IfBlock;
use lexer::Token;
use lexer::Token::{Identifier, StringLiteral, NumberLiteral, Comparison};
use lexer::ComparisonOperator;
use lexer::ComparisonOperator::{
    Equals, NotEquals,
    LessThan, GreaterThan,
    LessThanEquals, GreaterThanEquals,
    Contains
};
use parser::parse;
use lexer::Element;
use lexer::Element::{Tag, Raw};
use std::collections::HashMap;

struct If<'a>{
    lh : Token,
    comparison : ComparisonOperator,
    rh : Token,
    if_true: Template<'a>,
    if_false: Option<Template<'a>>
}

impl<'a> If<'a>{
    fn compare(&self, context: &HashMap<String, Value>) -> Result<bool, &'static str>{
        match (&self.lh, &self.rh)  {
            (&NumberLiteral(a), &NumberLiteral(b)) => Ok(compare_numbers(a, b, &self.comparison)),
            (&Identifier(ref var), &NumberLiteral(b)) => {
                match context.get(var.as_slice()) {
                    Some(&Value::Num(a)) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            },
            (&NumberLiteral(a), &Identifier(ref var)) => {
                match context.get(var.as_slice()) {
                    Some(&Value::Num(b)) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            }
            (&Identifier(ref var_a), &Identifier(ref var_b)) => {
                match (context.get(var_a.as_slice()), context.get(var_b.as_slice())) {
                    (Some(&Value::Num(a)), Some(&Value::Num(b))) => Ok(compare_numbers(a, b, &self.comparison)),
                    _ => Err("not comparable")
                }
            }
            (_, _) => Err("not implemented yet!") // TODO
        }
    }
}

fn compare_numbers(a : f32, b : f32, comparison : &ComparisonOperator) -> bool{
    match comparison {
        &Equals => a == b,
        &NotEquals => a != b,
        &LessThan => a < b,
        &GreaterThan => a > b,
        &LessThanEquals => a <= b,
        &GreaterThanEquals => a >= b,
        &Contains => false, // TODO!!!
    }
}

impl<'a> Renderable for If<'a>{
    fn render(&self, context: &HashMap<String, Value>) -> Option<String>{
        if self.compare(context).unwrap_or(false){
            self.if_true.render(context)
        }else{
            match self.if_false {
                Some(ref template) => template.render(context),
                _ => None
            }
        }
    }
}

impl<'a> Block for IfBlock<'a>{
    fn initialize<'a>(&'a self, tag_name: &str, arguments: &[Token], tokens: Vec<Element>, options : &'a LiquidOptions<'a>) -> Box<Renderable>{
        let mut args = arguments.iter();

        let lh = match args.next() {
            Some(&StringLiteral(ref x)) => StringLiteral(x.clone()),
            Some(&NumberLiteral(ref x)) => NumberLiteral(x.clone()),
            Some(&Identifier(ref x)) => Identifier(x.clone()),
            x => panic!("Expected a value, found {}", x)
        };

        let comp = match args.next() {
            Some(&Comparison(ref x)) => x.clone(),
            x => panic!("Expected a comparison operator, found {}", x)
        };

        let rh = match args.next() {
            Some(&StringLiteral(ref x)) => StringLiteral(x.clone()),
            Some(&NumberLiteral(ref x)) => NumberLiteral(x.clone()),
            Some(&Identifier(ref x)) => Identifier(x.clone()),
            x => panic!("Expected a value, found {}", x)
        };

        let else_block = vec![Identifier("else".to_string())];

        let if_true_tokens = tokens.iter().take_while(|&x| match x  {
            &Tag(ref eb, _) => *eb != else_block,
            _ => true
        }).map(|x| x.clone()).collect();

        let if_false_tokens : Vec<Element> = tokens.iter().skip_while(|&x| match x  {
            &Tag(ref eb, _) => *eb != else_block,
            _ => true
        }).map(|x| x.clone()).collect();

        let if_false = if if_false_tokens.len() > 0 {
            Some(Template::new(parse(if_false_tokens, options)))
        }else{
            None
        };

        box If{
            lh : lh,
            comparison : comp,
            rh : rh,
            if_true: Template::new(parse(if_true_tokens, options)),
            if_false: if_false
        } as Box<Renderable>
    }
}

#[test]
fn test_if() {
    let block = IfBlock;
    let options = LiquidOptions{blocks: HashMap::new(), tags: HashMap::new()};
    let if_tag = block.initialize("if", vec![NumberLiteral(5f32), Comparison(LessThan), NumberLiteral(6f32)][0..], vec![Raw("if true".to_string())], &options);
    assert_eq!(if_tag.render(&HashMap::new()).unwrap(), "if true".to_string());

    let else_tag = block.initialize("if", vec![NumberLiteral(7f32), Comparison(LessThan), NumberLiteral(6f32)][0..], vec![Raw("if true".to_string()), Tag(vec![Identifier("else".to_string())], "".to_string()), Raw("if false".to_string())], &options);
    assert_eq!(else_tag.render(&HashMap::new()).unwrap(), "if false".to_string());
}

<|endoftext|>