<commit_before>extern crate xml;

use std::env;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Write, stderr};
use std::path::Path;
use std::sync::{Once, ONCE_INIT};

use xml::name::OwnedName;
use xml::common::Position;
use xml::reader::{Result, XmlEvent, ParserConfig, EventReader};

/// Dummy function that opens a file, parses it, and returns a `Result`.
/// There can be IO errors (from `File::open`) and XML errors (from the parser).
/// Having `impl From<std::io::Error> for xml::reader::Error` allows the user to
/// do this without defining their own error type.
#[allow(dead_code)]
fn count_event_in_file(name: &Path) -> Result<usize> {
    let mut event_count = 0;
    for event in EventReader::new(BufReader::new(try!(File::open(name)))) {
        try!(event);
        event_count += 1;
    }
    Ok(event_count)
}

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_1_full() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );
}

#[test]
fn sample_2_short() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_2_full() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );
}

#[test]
fn sample_3_short() {
    test(
        include_bytes!("documents/sample_3.xml"),
        include_bytes!("documents/sample_3_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        true
    );
}

#[test]
fn sample_3_full() {
    test(
        include_bytes!("documents/sample_3.xml"),
        include_bytes!("documents/sample_3_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        true
    );
}

#[test]
fn sample_4_short() {
    test(
        include_bytes!("documents/sample_4.xml"),
        include_bytes!("documents/sample_4_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_4_full() {
    test(
        include_bytes!("documents/sample_4.xml"),
        include_bytes!("documents/sample_4_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );

}

#[test]
fn sample_5_short() {
    test(
        include_bytes!("documents/sample_5.xml"),
        include_bytes!("documents/sample_5_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true)
            .add_entity("nbsp", " ")
            .add_entity("copy", "©")
            .add_entity("NotEqualTilde", "≂̸"),
        false
    );
}

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"1:6 Unexpected end of stream: no root element found"#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn bad_1() {
    test(
        br#"<?xml&.,"#,
        br#"1:6 Unexpected token: <?xml&"#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn dashes_in_comments() {
    test(
        br#"<hello/>"#,
        br#"
            |1:14 Unexpected token '--' before ' '
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello/>"#,
        br#"
            |1:14 Unexpected token '--' before '-'
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            |1:2 StartDocument(1.0, UTF-8)
            |1:2 StartElement(a)
            |1:6 StartElement(b)
            |1:6 EndElement(b)
            |1:10 EndElement(a)
            |1:14 EndDocument
        "#,
        ParserConfig::new()
            .trim_whitespace(true),
        true
    );
}

#[test]
fn issue_83_duplicate_attributes() {
    test(
        br#"<hello><some-tag a='10' a="20"></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |1:30 Attribute 'a' is redefined
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(
        r#"<hello>&𤶼;</hello>"#.as_bytes(),
        r#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |1:10 Unexpected entity: 𤶼
        "#.as_bytes(),  // FIXME: it shouldn't be 10, looks like indices are off slightly
        ParserConfig::new(),
        false
    )
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |CData("Foo [Bar]")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("-- ")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("--")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("-->")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |CData("--")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn issue_attribues_have_no_default_namespace () {
    test(
        br#"<hello xmlns="urn:foo" x="y"/>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement({urn:foo}hello [x="y"])
            |EndElement({urn:foo}hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );
}

static START: Once = ONCE_INIT;
static mut PRINT: bool = false;

// clones a lot but that's fine
fn trim_until_bar(s: String) -> String {
    match s.trim() {
        ts if ts.starts_with('|') => return ts[1..].to_owned(),
        _ => {}
    }
    s
}

fn test(input: &[u8], output: &[u8], config: ParserConfig, test_position: bool) {
    // If PRINT_SPEC env variable is set, print the lines
    // to stderr instead of comparing with the output
    // it can be used like this:
    // PRINT_SPEC=1 cargo test --test event_reader sample_1_full 2> sample_1_full.txt
    START.call_once(|| {
        for (key, value) in env::vars() {
            if key == "PRINT_SPEC" && value == "1" {
                unsafe { PRINT = true; }
            }
        }
    });

    let mut reader = config.create_reader(input);
    let mut spec_lines = BufReader::new(output).lines()
        .map(|line| line.unwrap())
        .enumerate()
        .map(|(i, line)| (i, trim_until_bar(line)))
        .filter(|&(_, ref line)| !line.trim().is_empty());

    loop {
        let e = reader.next();
        let line =
            if test_position {
                format!("{} {}", reader.position(), Event(&e))
            } else {
                format!("{}", Event(&e))
            };

        if unsafe { PRINT } {
            writeln!(&mut stderr(), "{}", line).unwrap();
        } else {
            if let Some((n, spec)) = spec_lines.next() {
                if line != spec {
                    const SPLITTER: &'static str = "-------------------";
                    panic!("\n{}\nUnexpected event at line {}:\nExpected: {}\nFound:    {}\n{}\n",
                           SPLITTER, n + 1, spec, line, SPLITTER);
                }
            } else {
                panic!("Unexpected event: {}", line);
            }
        }

        match e {
            Ok(XmlEvent::EndDocument) | Err(_) => break,
            _ => {},
        }
    }
}

// Here we define our own string representation of events so we don't depend
// on the specifics of Display implementation for XmlEvent and OwnedName.

struct Name<'a>(&'a OwnedName);

impl <'a> fmt::Display for Name<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref namespace) = self.0.namespace {
            try! { write!(f, "{{{}}}", namespace) }
        }

        if let Some(ref prefix) = self.0.prefix {
            try! { write!(f, "{}:", prefix) }
        }

        write!(f, "{}", self.0.local_name)
    }
}

struct Event<'a>(&'a Result<XmlEvent>);

impl<'a> fmt::Display for Event<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = String::new();
        match *self.0 {
            Ok(ref e) => match *e {
                XmlEvent::StartDocument { ref version, ref encoding, .. } =>
                    write!(f, "StartDocument({}, {})", version, encoding),
                XmlEvent::EndDocument =>
                    write!(f, "EndDocument"),
                XmlEvent::ProcessingInstruction { ref name, ref data } =>
                    write!(f, "ProcessingInstruction({}={:?})", name,
                        data.as_ref().unwrap_or(&empty)),
                XmlEvent::StartElement { ref name, ref attributes, .. } => {
                    if attributes.is_empty() {
                        write!(f, "StartElement({})", Name(name))
                    }
                    else {
                        let attrs: Vec<_> = attributes.iter()
                            .map(|a| format!("{}={:?}", Name(&a.name), a.value)) .collect();
                        write!(f, "StartElement({} [{}])", Name(name), attrs.join(", "))
                    }
                },
                XmlEvent::EndElement { ref name } =>
                    write!(f, "EndElement({})", Name(name)),
                XmlEvent::Comment(ref data) =>
                    write!(f, "Comment({:?})", data),
                XmlEvent::CData(ref data) =>
                    write!(f, "CData({:?})", data),
                XmlEvent::Characters(ref data) =>
                    write!(f, "Characters({:?})", data),
                XmlEvent::Whitespace(ref data) =>
                    write!(f, "Whitespace({:?})", data),
            },
            Err(ref e) => e.fmt(f),
        }
    }
}
<commit_msg>Fix unit tests to run with changed Debug format for String<commit_after>extern crate xml;

use std::env;
use std::fmt;
use std::fs::File;
use std::io::{BufRead, BufReader, Write, stderr};
use std::path::Path;
use std::sync::{Once, ONCE_INIT};

use xml::name::OwnedName;
use xml::common::Position;
use xml::reader::{Result, XmlEvent, ParserConfig, EventReader};

/// Dummy function that opens a file, parses it, and returns a `Result`.
/// There can be IO errors (from `File::open`) and XML errors (from the parser).
/// Having `impl From<std::io::Error> for xml::reader::Error` allows the user to
/// do this without defining their own error type.
#[allow(dead_code)]
fn count_event_in_file(name: &Path) -> Result<usize> {
    let mut event_count = 0;
    for event in EventReader::new(BufReader::new(try!(File::open(name)))) {
        try!(event);
        event_count += 1;
    }
    Ok(event_count)
}

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_1_full() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );
}

#[test]
fn sample_2_short() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_2_full() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );
}

#[test]
fn sample_3_short() {
    test(
        include_bytes!("documents/sample_3.xml"),
        include_bytes!("documents/sample_3_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        true
    );
}

#[test]
fn sample_3_full() {
    test(
        include_bytes!("documents/sample_3.xml"),
        include_bytes!("documents/sample_3_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        true
    );
}

#[test]
fn sample_4_short() {
    test(
        include_bytes!("documents/sample_4.xml"),
        include_bytes!("documents/sample_4_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true),
        false
    );
}

#[test]
fn sample_4_full() {
    test(
        include_bytes!("documents/sample_4.xml"),
        include_bytes!("documents/sample_4_full.txt"),
        ParserConfig::new()
            .ignore_comments(false)
            .whitespace_to_characters(false)
            .cdata_to_characters(false)
            .trim_whitespace(false)
            .coalesce_characters(false),
        false
    );

}

#[test]
fn sample_5_short() {
    test(
        include_bytes!("documents/sample_5.xml"),
        include_bytes!("documents/sample_5_short.txt"),
        ParserConfig::new()
            .ignore_comments(true)
            .whitespace_to_characters(true)
            .cdata_to_characters(true)
            .trim_whitespace(true)
            .coalesce_characters(true)
            .add_entity("nbsp", " ")
            .add_entity("copy", "©")
            .add_entity("NotEqualTilde", "≂̸"),
        false
    );
}

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"1:6 Unexpected end of stream: no root element found"#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn bad_1() {
    test(
        br#"<?xml&.,"#,
        br#"1:6 Unexpected token: <?xml&"#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn dashes_in_comments() {
    test(
        br#"<hello/>"#,
        br#"
            |1:14 Unexpected token '--' before ' '
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello/>"#,
        br#"
            |1:14 Unexpected token '--' before '-'
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            |1:2 StartDocument(1.0, UTF-8)
            |1:2 StartElement(a)
            |1:6 StartElement(b)
            |1:6 EndElement(b)
            |1:10 EndElement(a)
            |1:14 EndDocument
        "#,
        ParserConfig::new()
            .trim_whitespace(true),
        true
    );
}

#[test]
fn issue_83_duplicate_attributes() {
    test(
        br#"<hello><some-tag a='10' a="20"></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |1:30 Attribute 'a' is redefined
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(
        r#"<hello>&𤶼;</hello>"#.as_bytes(),
        r#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |1:10 Unexpected entity: 𤶼
        "#.as_bytes(),  // FIXME: it shouldn't be 10, looks like indices are off slightly
        ParserConfig::new(),
        false
    )
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |CData("Foo [Bar]")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("-- ")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("--")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |Characters("-->")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement(hello)
            |CData("--")
            |EndElement(hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );
}

#[test]
fn issue_attribues_have_no_default_namespace () {
    test(
        br#"<hello xmlns="urn:foo" x="y"/>"#,
        br#"
            |StartDocument(1.0, UTF-8)
            |StartElement({urn:foo}hello [x="y"])
            |EndElement({urn:foo}hello)
            |EndDocument
        "#,
        ParserConfig::new(),
        false
    );
}

static START: Once = ONCE_INIT;
static mut PRINT: bool = false;

// clones a lot but that's fine
fn trim_until_bar(s: String) -> String {
    match s.trim() {
        ts if ts.starts_with('|') => return ts[1..].to_owned(),
        _ => {}
    }
    s
}

fn test(input: &[u8], output: &[u8], config: ParserConfig, test_position: bool) {
    // If PRINT_SPEC env variable is set, print the lines
    // to stderr instead of comparing with the output
    // it can be used like this:
    // PRINT_SPEC=1 cargo test --test event_reader sample_1_full 2> sample_1_full.txt
    START.call_once(|| {
        for (key, value) in env::vars() {
            if key == "PRINT_SPEC" && value == "1" {
                unsafe { PRINT = true; }
            }
        }
    });

    let mut reader = config.create_reader(input);
    let mut spec_lines = BufReader::new(output).lines()
        .map(|line| line.unwrap())
        .enumerate()
        .map(|(i, line)| (i, trim_until_bar(line)))
        .filter(|&(_, ref line)| !line.trim().is_empty());

    loop {
        let e = reader.next();
        let line =
            if test_position {
                format!("{} {}", reader.position(), Event(&e))
            } else {
                format!("{}", Event(&e))
            };

        if unsafe { PRINT } {
            writeln!(&mut stderr(), "{}", line).unwrap();
        } else {
            if let Some((n, spec)) = spec_lines.next() {
                if line != spec {
                    const SPLITTER: &'static str = "-------------------";
                    panic!("\n{}\nUnexpected event at line {}:\nExpected: {}\nFound:    {}\n{}\n",
                           SPLITTER, n + 1, spec, line, std::str::from_utf8(output).unwrap());
                }
            } else {
                panic!("Unexpected event: {}", line);
            }
        }

        match e {
            Ok(XmlEvent::EndDocument) | Err(_) => break,
            _ => {},
        }
    }
}

// Here we define our own string representation of events so we don't depend
// on the specifics of Display implementation for XmlEvent and OwnedName.

struct Name<'a>(&'a OwnedName);

impl <'a> fmt::Display for Name<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if let Some(ref namespace) = self.0.namespace {
            try! { write!(f, "{{{}}}", namespace) }
        }

        if let Some(ref prefix) = self.0.prefix {
            try! { write!(f, "{}:", prefix) }
        }

        write!(f, "{}", self.0.local_name)
    }
}

struct Event<'a>(&'a Result<XmlEvent>);

impl<'a> fmt::Display for Event<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let empty = String::new();
        match *self.0 {
            Ok(ref e) => match *e {
                XmlEvent::StartDocument { ref version, ref encoding, .. } =>
                    write!(f, "StartDocument({}, {})", version, encoding),
                XmlEvent::EndDocument =>
                    write!(f, "EndDocument"),
                XmlEvent::ProcessingInstruction { ref name, ref data } =>
                    write!(f, "ProcessingInstruction({}={:?})", name,
                        data.as_ref().unwrap_or(&empty)),
                XmlEvent::StartElement { ref name, ref attributes, .. } => {
                    if attributes.is_empty() {
                        write!(f, "StartElement({})", Name(name))
                    }
                    else {
                        let attrs: Vec<_> = attributes.iter()
                            .map(|a| format!("{}={:?}", Name(&a.name), a.value)) .collect();
                        write!(f, "StartElement({} [{}])", Name(name), attrs.join(", "))
                    }
                },
                XmlEvent::EndElement { ref name } =>
                    write!(f, "EndElement({})", Name(name)),
                XmlEvent::Comment(ref data) =>
                    write!(f, r#"Comment("{}")"#, data.escape_debug()),
                XmlEvent::CData(ref data) =>
                    write!(f, r#"CData("{}")"#, data.escape_debug()),
                XmlEvent::Characters(ref data) =>
                    write!(f, r#"Characters("{}")"#, data.escape_debug()),
                XmlEvent::Whitespace(ref data) =>
                    write!(f, r#"Whitespace("{}")"#, data.escape_debug()),
            },
            Err(ref e) => e.fmt(f),
        }
    }
}
<|endoftext|>


<commit_before>
extern crate env_logger;

extern crate gluon;

use gluon::vm::api::{Hole, OpaqueValue};
use gluon::{new_vm, Compiler, Thread};

use std::io::Read;
use std::fmt;
use std::fs::{File, read_dir};
use std::path::PathBuf;
use std::error::Error;

#[derive(Debug)]
pub struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

#[test]
fn main() {
    if let Err(err) = main_() {
        assert!(false, "{}", err);
    }
}

fn test_files(path: &str) -> Result<Box<Iterator<Item = PathBuf>>, Box<Error>> {
    let dir = try!(read_dir(path));
    Ok(Box::new(dir.filter_map(|f| {
        f.ok()
            .and_then(|f| {
                let path = f.path();
                if path.extension().and_then(|e| e.to_str()) == Some("hs") {
                    Some(path)
                } else {
                    None
                }
            })
    })))
}

fn main_() -> Result<(), Box<Error>> {
    let vm = new_vm();
    let mut compiler = Compiler::new();
    try!(compiler.load_file(&vm, "std/prelude.glu"));
    let mut text = String::new();
    let _ = ::env_logger::init();
    for filename in try!(test_files("tests/pass")) {
        let mut file = try!(File::open(&filename));
        text.clear();
        try!(file.read_to_string(&mut text));
        let name = filename.to_str().unwrap_or("<unknown>");
        println!("test {}", name);
        try!(compiler.run_expr::<OpaqueValue<&Thread, Hole>>(&vm, name, &text));
    }
    for filename in try!(test_files("tests/fail")) {
        let mut file = try!(File::open(&filename));
        text.clear();
        try!(file.read_to_string(&mut text));
        let name = filename.to_str().unwrap_or("<unknown>");
        println!("test {}", name);
        match compiler.run_expr::<OpaqueValue<&Thread, Hole>>(&vm, name, &text) {
            Ok(x) => {
                return Err(StringError(format!("Expected test '{}' to fail got {:?}",
                                               filename.to_str().unwrap(),
                                               x))
                    .into())
            }
            Err(_) => (),
        }
    }
    Ok(())
}
<commit_msg>test: Re-enable the standalone pass and fail test<commit_after>
extern crate env_logger;

extern crate gluon;

use gluon::vm::api::{Hole, OpaqueValue};
use gluon::{new_vm, Compiler, Thread};

use std::io::Read;
use std::fmt;
use std::fs::{File, read_dir};
use std::path::PathBuf;
use std::error::Error;

#[derive(Debug)]
pub struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl Error for StringError {
    fn description(&self) -> &str {
        &self.0
    }
}

#[test]
fn main() {
    if let Err(err) = main_() {
        assert!(false, "{}", err);
    }
}

fn test_files(path: &str) -> Result<Vec<PathBuf>, Box<Error>> {
    let dir = try!(read_dir(path));
    let paths: Vec<_> = dir.filter_map(|f| {
            f.ok()
                .and_then(|f| {
                    let path = f.path();
                    if path.extension().and_then(|e| e.to_str()) == Some("glu") {
                        Some(path)
                    } else {
                        None
                    }
                })
        })
        .collect();
    assert!(!paths.is_empty(), "Expected test files");
    Ok(paths)
}

fn main_() -> Result<(), Box<Error>> {
    let vm = new_vm();
    let mut compiler = Compiler::new();
    try!(compiler.load_file(&vm, "std/prelude.glu"));
    let mut text = String::new();
    let _ = ::env_logger::init();
    for filename in try!(test_files("tests/pass")) {
        let mut file = try!(File::open(&filename));
        text.clear();
        try!(file.read_to_string(&mut text));
        let name = filename.to_str().unwrap_or("<unknown>");
        println!("test {}", name);
        try!(compiler.run_expr::<OpaqueValue<&Thread, Hole>>(&vm, name, &text));
    }
    for filename in try!(test_files("tests/fail")) {
        let mut file = try!(File::open(&filename));
        text.clear();
        try!(file.read_to_string(&mut text));
        let name = filename.to_str().unwrap_or("<unknown>");
        println!("test {}", name);
        match compiler.run_expr::<OpaqueValue<&Thread, Hole>>(&vm, name, &text) {
            Ok(x) => {
                return Err(StringError(format!("Expected test '{}' to fail got {:?}",
                                               filename.to_str().unwrap(),
                                               x))
                    .into())
            }
            Err(_) => (),
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 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.

#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private))]

#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate syntax;

use std::rc::Rc;

use syntax::ast::{self, TokenTree, Generics, Expr};
use syntax::codemap::{DUMMY_SP, Spanned, dummy_spanned};
use syntax::ext::base::ExtCtxt;
use syntax::parse::{self, classify, parse_tts_from_source_str, token};
use syntax::parse::parser::Parser;
use syntax::print::pprust;
use syntax::ptr::P;

pub trait ToTokens {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>;
}

impl ToTokens for TokenTree {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec!(self.clone())
    }
}

impl<'a, T: ToTokens> ToTokens for &'a T {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        (**self).to_tokens(cx)
    }
}

impl<'a, T: ToTokens> ToTokens for &'a [T] {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        self.iter()
            .flat_map(|t| t.to_tokens(cx).into_iter())
            .collect()
    }
}

impl<T: ToTokens> ToTokens for Vec<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        self.iter().flat_map(|t| t.to_tokens(cx).into_iter()).collect()
    }
}

impl<T: ToTokens> ToTokens for Spanned<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        // FIXME: use the span?
        self.node.to_tokens(cx)
    }
}

impl<T: ToTokens> ToTokens for Option<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        match self {
            &Some(ref t) => t.to_tokens(cx),
            &None => Vec::new(),
        }
    }
}

impl ToTokens for ast::Ident {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Ident(*self, token::Plain))]
    }
}

impl ToTokens for ast::Path {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtPath(Box::new(self.clone()))))]
    }
}

impl ToTokens for ast::Ty {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTy(P(self.clone()))))]
    }
}

impl ToTokens for P<ast::Ty> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTy(self.clone())))]
    }
}

impl ToTokens for P<ast::Block> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtBlock(self.clone())))]
    }
}

impl ToTokens for P<ast::Item> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtItem(self.clone())))]
    }
}

impl ToTokens for P<ast::ImplItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtImplItem(self.clone())))]
    }
}

impl ToTokens for P<ast::TraitItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTraitItem(self.clone())))]
    }
}

impl ToTokens for ast::Generics {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let s = pprust::generics_to_string(self);

        parse_tts_from_source_str("<quote expansion>".to_string(), s, cx.cfg(), cx.parse_sess())
    }
}

impl ToTokens for ast::WhereClause {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let s = pprust::to_string(|s| {
            s.print_where_clause(&self)
        });

        parse_tts_from_source_str("<quote expansion>".to_string(), s, cx.cfg(), cx.parse_sess())
    }
}

impl ToTokens for P<ast::Stmt> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut tts = vec![
            ast::TokenTree::Token(self.span, token::Interpolated(token::NtStmt(self.clone())))
        ];

        // Some statements require a trailing semicolon.
        if classify::stmt_ends_with_semi(&self.node) {
            tts.push(ast::TokenTree::Token(self.span, token::Semi));
        }

        tts
    }
}

impl ToTokens for P<ast::Expr> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtExpr(self.clone())))]
    }
}

impl ToTokens for P<ast::Pat> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtPat(self.clone())))]
    }
}

impl ToTokens for ast::Arm {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtArm(self.clone())))]
    }
}

macro_rules! impl_to_tokens_slice {
    ($t: ty, $sep: expr) => {
        impl ToTokens for [$t] {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let mut v = vec![];
                for (i, x) in self.iter().enumerate() {
                    if i > 0 {
                        v.extend($sep.iter().cloned());
                    }
                    v.extend(x.to_tokens(cx));
                }
                v
            }
        }
    };
}

impl_to_tokens_slice! { ast::Ty, [ast::TokenTree::Token(DUMMY_SP, token::Comma)] }
impl_to_tokens_slice! { P<ast::Item>, [] }

impl ToTokens for P<ast::MetaItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtMeta(self.clone())))]
    }
}

impl ToTokens for ast::Attribute {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        // FIXME: The spans could be better
        r.push(ast::TokenTree::Token(self.span, token::Pound));
        if self.node.style == ast::AttrStyle::Inner {
            r.push(ast::TokenTree::Token(self.span, token::Not));
        }
        r.push(ast::TokenTree::Delimited(self.span, Rc::new(ast::Delimited {
            delim: token::Bracket,
            open_span: self.span,
            tts: self.node.value.to_tokens(cx),
            close_span: self.span,
        })));
        r
    }
}

impl ToTokens for str {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let lit = ast::LitStr(
            token::intern_and_get_ident(self), ast::CookedStr);
        dummy_spanned(lit).to_tokens(cx)
    }
}

impl ToTokens for () {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Delimited(DUMMY_SP, Rc::new(ast::Delimited {
            delim: token::Paren,
            open_span: DUMMY_SP,
            tts: vec![],
            close_span: DUMMY_SP,
        }))]
    }
}

impl ToTokens for ast::Lit {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        // FIXME: This is wrong
        P(ast::Expr {
            id: ast::DUMMY_NODE_ID,
            node: ast::ExprLit(P(self.clone())),
            span: DUMMY_SP,
        }).to_tokens(cx)
    }
}

impl ToTokens for bool {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        dummy_spanned(ast::LitBool(*self)).to_tokens(cx)
    }
}

impl ToTokens for char {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        dummy_spanned(ast::LitChar(*self)).to_tokens(cx)
    }
}

macro_rules! impl_to_tokens_int {
    (signed, $t:ty, $tag:expr) => (
        impl ToTokens for $t {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let lit = ast::LitInt(*self as u64, ast::SignedIntLit($tag,
                                                                      ast::Sign::new(*self)));
                dummy_spanned(lit).to_tokens(cx)
            }
        }
    );
    (unsigned, $t:ty, $tag:expr) => (
        impl ToTokens for $t {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let lit = ast::LitInt(*self as u64, ast::UnsignedIntLit($tag));
                dummy_spanned(lit).to_tokens(cx)
            }
        }
    );
}

impl_to_tokens_int! { signed, isize, ast::TyIs }
impl_to_tokens_int! { signed, i8,  ast::TyI8 }
impl_to_tokens_int! { signed, i16, ast::TyI16 }
impl_to_tokens_int! { signed, i32, ast::TyI32 }
impl_to_tokens_int! { signed, i64, ast::TyI64 }

impl_to_tokens_int! { unsigned, usize, ast::TyUs }
impl_to_tokens_int! { unsigned, u8,   ast::TyU8 }
impl_to_tokens_int! { unsigned, u16,  ast::TyU16 }
impl_to_tokens_int! { unsigned, u32,  ast::TyU32 }
impl_to_tokens_int! { unsigned, u64,  ast::TyU64 }

pub trait ExtParseUtils {
    fn parse_item(&self, s: String) -> P<ast::Item>;
    fn parse_expr(&self, s: String) -> P<ast::Expr>;
    fn parse_stmt(&self, s: String) -> P<ast::Stmt>;
    fn parse_tts(&self, s: String) -> Vec<ast::TokenTree>;
}

impl<'a> ExtParseUtils for ExtCtxt<'a> {

    fn parse_item(&self, s: String) -> P<ast::Item> {
        parse::parse_item_from_source_str(
            "<quote expansion>".to_string(),
            s,
            self.cfg(),
            self.parse_sess()).expect("parse error")
    }

    fn parse_stmt(&self, s: String) -> P<ast::Stmt> {
        parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
                                          s,
                                          self.cfg(),
                                          self.parse_sess()).expect("parse error")
    }

    fn parse_expr(&self, s: String) -> P<ast::Expr> {
        parse::parse_expr_from_source_str("<quote expansion>".to_string(),
                                          s,
                                          self.cfg(),
                                          self.parse_sess())
    }

    fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
        parse::parse_tts_from_source_str("<quote expansion>".to_string(),
                                         s,
                                         self.cfg(),
                                         self.parse_sess())
    }
}

macro_rules! panictry {
    ($e:expr) => ({
        match $e {
            Ok(e) => e,
            Err(err) => panic!(err)
        }
    })
}

pub fn parse_expr_panic(parser: &mut Parser) -> P<ast::Expr> {
        panictry!(parser.parse_expr())
}

pub fn parse_item_panic(parser: &mut Parser) -> Option<P<ast::Item>> {
        panictry!(parser.parse_item())
}

pub fn parse_pat_panic(parser: &mut Parser) -> P<ast::Pat> {
        panictry!(parser.parse_pat())
}

pub fn parse_arm_panic(parser: &mut Parser) -> ast::Arm {
        panictry!(parser.parse_arm())
}

pub fn parse_ty_panic(parser: &mut Parser) -> P<ast::Ty> {
        panictry!(parser.parse_ty())
}

pub fn parse_stmt_panic(parser: &mut Parser) -> Option<P<ast::Stmt>> {
    panictry!(parser.parse_stmt())
}

pub fn parse_attribute_panic(parser: &mut Parser, permit_inner: bool) -> ast::Attribute {
        panictry!(parser.parse_attribute(permit_inner))
}
<commit_msg>Update for rust-lang/rust#29850<commit_after>// Copyright 2012-2014 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.

#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private))]

#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;

#[cfg(not(feature = "with-syntex"))]
extern crate syntax;

use std::rc::Rc;

use syntax::ast::{self, TokenTree, Generics, Expr};
use syntax::codemap::{DUMMY_SP, Spanned, dummy_spanned};
use syntax::ext::base::ExtCtxt;
use syntax::parse::{self, classify, parse_tts_from_source_str, token};
use syntax::parse::parser::Parser;
use syntax::print::pprust;
use syntax::ptr::P;

pub trait ToTokens {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>;
}

impl ToTokens for TokenTree {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec!(self.clone())
    }
}

impl<'a, T: ToTokens> ToTokens for &'a T {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        (**self).to_tokens(cx)
    }
}

impl<'a, T: ToTokens> ToTokens for &'a [T] {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        self.iter()
            .flat_map(|t| t.to_tokens(cx).into_iter())
            .collect()
    }
}

impl<T: ToTokens> ToTokens for Vec<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        self.iter().flat_map(|t| t.to_tokens(cx).into_iter()).collect()
    }
}

impl<T: ToTokens> ToTokens for Spanned<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        // FIXME: use the span?
        self.node.to_tokens(cx)
    }
}

impl<T: ToTokens> ToTokens for Option<T> {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        match self {
            &Some(ref t) => t.to_tokens(cx),
            &None => Vec::new(),
        }
    }
}

impl ToTokens for ast::Ident {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Ident(*self, token::Plain))]
    }
}

impl ToTokens for ast::Path {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtPath(Box::new(self.clone()))))]
    }
}

impl ToTokens for ast::Ty {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTy(P(self.clone()))))]
    }
}

impl ToTokens for P<ast::Ty> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTy(self.clone())))]
    }
}

impl ToTokens for P<ast::Block> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtBlock(self.clone())))]
    }
}

impl ToTokens for P<ast::Item> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtItem(self.clone())))]
    }
}

impl ToTokens for P<ast::ImplItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtImplItem(self.clone())))]
    }
}

impl ToTokens for P<ast::TraitItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtTraitItem(self.clone())))]
    }
}

impl ToTokens for ast::Generics {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let s = pprust::generics_to_string(self);

        parse_tts_from_source_str("<quote expansion>".to_string(), s, cx.cfg(), cx.parse_sess())
    }
}

impl ToTokens for ast::WhereClause {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let s = pprust::to_string(|s| {
            s.print_where_clause(&self)
        });

        parse_tts_from_source_str("<quote expansion>".to_string(), s, cx.cfg(), cx.parse_sess())
    }
}

impl ToTokens for P<ast::Stmt> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut tts = vec![
            ast::TokenTree::Token(self.span, token::Interpolated(token::NtStmt(self.clone())))
        ];

        // Some statements require a trailing semicolon.
        if classify::stmt_ends_with_semi(&self.node) {
            tts.push(ast::TokenTree::Token(self.span, token::Semi));
        }

        tts
    }
}

impl ToTokens for P<ast::Expr> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtExpr(self.clone())))]
    }
}

impl ToTokens for P<ast::Pat> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(self.span, token::Interpolated(token::NtPat(self.clone())))]
    }
}

impl ToTokens for ast::Arm {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtArm(self.clone())))]
    }
}

macro_rules! impl_to_tokens_slice {
    ($t: ty, $sep: expr) => {
        impl ToTokens for [$t] {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let mut v = vec![];
                for (i, x) in self.iter().enumerate() {
                    if i > 0 {
                        v.extend($sep.iter().cloned());
                    }
                    v.extend(x.to_tokens(cx));
                }
                v
            }
        }
    };
}

impl_to_tokens_slice! { ast::Ty, [ast::TokenTree::Token(DUMMY_SP, token::Comma)] }
impl_to_tokens_slice! { P<ast::Item>, [] }

impl ToTokens for P<ast::MetaItem> {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtMeta(self.clone())))]
    }
}

impl ToTokens for ast::Attribute {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let mut r = vec![];
        // FIXME: The spans could be better
        r.push(ast::TokenTree::Token(self.span, token::Pound));
        if self.node.style == ast::AttrStyle::Inner {
            r.push(ast::TokenTree::Token(self.span, token::Not));
        }
        r.push(ast::TokenTree::Delimited(self.span, Rc::new(ast::Delimited {
            delim: token::Bracket,
            open_span: self.span,
            tts: self.node.value.to_tokens(cx),
            close_span: self.span,
        })));
        r
    }
}

impl ToTokens for str {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        let lit = ast::LitStr(
            token::intern_and_get_ident(self), ast::CookedStr);
        dummy_spanned(lit).to_tokens(cx)
    }
}

impl ToTokens for () {
    fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
        vec![ast::TokenTree::Delimited(DUMMY_SP, Rc::new(ast::Delimited {
            delim: token::Paren,
            open_span: DUMMY_SP,
            tts: vec![],
            close_span: DUMMY_SP,
        }))]
    }
}

impl ToTokens for ast::Lit {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        // FIXME: This is wrong
        P(ast::Expr {
            id: ast::DUMMY_NODE_ID,
            node: ast::ExprLit(P(self.clone())),
            span: DUMMY_SP,
            attrs: None,
        }).to_tokens(cx)
    }
}

impl ToTokens for bool {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        dummy_spanned(ast::LitBool(*self)).to_tokens(cx)
    }
}

impl ToTokens for char {
    fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
        dummy_spanned(ast::LitChar(*self)).to_tokens(cx)
    }
}

macro_rules! impl_to_tokens_int {
    (signed, $t:ty, $tag:expr) => (
        impl ToTokens for $t {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let lit = ast::LitInt(*self as u64, ast::SignedIntLit($tag,
                                                                      ast::Sign::new(*self)));
                dummy_spanned(lit).to_tokens(cx)
            }
        }
    );
    (unsigned, $t:ty, $tag:expr) => (
        impl ToTokens for $t {
            fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
                let lit = ast::LitInt(*self as u64, ast::UnsignedIntLit($tag));
                dummy_spanned(lit).to_tokens(cx)
            }
        }
    );
}

impl_to_tokens_int! { signed, isize, ast::TyIs }
impl_to_tokens_int! { signed, i8,  ast::TyI8 }
impl_to_tokens_int! { signed, i16, ast::TyI16 }
impl_to_tokens_int! { signed, i32, ast::TyI32 }
impl_to_tokens_int! { signed, i64, ast::TyI64 }

impl_to_tokens_int! { unsigned, usize, ast::TyUs }
impl_to_tokens_int! { unsigned, u8,   ast::TyU8 }
impl_to_tokens_int! { unsigned, u16,  ast::TyU16 }
impl_to_tokens_int! { unsigned, u32,  ast::TyU32 }
impl_to_tokens_int! { unsigned, u64,  ast::TyU64 }

pub trait ExtParseUtils {
    fn parse_item(&self, s: String) -> P<ast::Item>;
    fn parse_expr(&self, s: String) -> P<ast::Expr>;
    fn parse_stmt(&self, s: String) -> P<ast::Stmt>;
    fn parse_tts(&self, s: String) -> Vec<ast::TokenTree>;
}

impl<'a> ExtParseUtils for ExtCtxt<'a> {

    fn parse_item(&self, s: String) -> P<ast::Item> {
        parse::parse_item_from_source_str(
            "<quote expansion>".to_string(),
            s,
            self.cfg(),
            self.parse_sess()).expect("parse error")
    }

    fn parse_stmt(&self, s: String) -> P<ast::Stmt> {
        parse::parse_stmt_from_source_str("<quote expansion>".to_string(),
                                          s,
                                          self.cfg(),
                                          self.parse_sess()).expect("parse error")
    }

    fn parse_expr(&self, s: String) -> P<ast::Expr> {
        parse::parse_expr_from_source_str("<quote expansion>".to_string(),
                                          s,
                                          self.cfg(),
                                          self.parse_sess())
    }

    fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
        parse::parse_tts_from_source_str("<quote expansion>".to_string(),
                                         s,
                                         self.cfg(),
                                         self.parse_sess())
    }
}

macro_rules! panictry {
    ($e:expr) => ({
        match $e {
            Ok(e) => e,
            Err(err) => panic!(err)
        }
    })
}

pub fn parse_expr_panic(parser: &mut Parser) -> P<ast::Expr> {
        panictry!(parser.parse_expr())
}

pub fn parse_item_panic(parser: &mut Parser) -> Option<P<ast::Item>> {
        panictry!(parser.parse_item())
}

pub fn parse_pat_panic(parser: &mut Parser) -> P<ast::Pat> {
        panictry!(parser.parse_pat())
}

pub fn parse_arm_panic(parser: &mut Parser) -> ast::Arm {
        panictry!(parser.parse_arm())
}

pub fn parse_ty_panic(parser: &mut Parser) -> P<ast::Ty> {
        panictry!(parser.parse_ty())
}

pub fn parse_stmt_panic(parser: &mut Parser) -> Option<P<ast::Stmt>> {
    panictry!(parser.parse_stmt())
}

pub fn parse_attribute_panic(parser: &mut Parser, permit_inner: bool) -> ast::Attribute {
        panictry!(parser.parse_attribute(permit_inner))
}
<|endoftext|>


<commit_before><commit_msg>Deal with vectorized! lifetimes within the macro<commit_after><|endoftext|>


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

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

#[link(name = "benchmark", vers = "alpha", author = "dwrensha")];

#[crate_type = "bin"];

extern mod capnp;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnp;
    use std;

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

    pub fn newReader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }

    pub fn newBufferedReader<R: std::io::Reader, T>(
        inputStream : &mut capnp::io::BufferedInputStream<R>,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnp;
    use std;
    use capnp::serialize_packed::{WritePackedWrapper, WritePacked};

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                          message: &capnp::message::MessageBuilder) {
        let mut w = WritePackedWrapper{writer: writer};
        w.writePackedMessage(message);
    }

    pub fn newReader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : &mut capnp::io::BufferedInputStream::new(inputStream)
            },
            options, cont)
    }

    pub fn newBufferedReader<R:std::io::Reader, T>(
        inputStream : &mut capnp::io::BufferedInputStream<R>,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : inputStream
            },
            options, cont)
    }

}

macro_rules! passByObject(
    ( $testcase:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnp::message::MessageBuilder::new_default();
                let mut messageRes = capnp::message::MessageBuilder::new_default();

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

                do request.asReader |requestReader| {
                    $testcase::handleRequest(requestReader, response);
                }

                do response.asReader |responseReader| {
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        fail!("Incorrect response.");
                    }
                }
            }
        });
    )

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

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

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

                {
                    let mut writer = std::io::mem::BufWriter::new(requestBytes);
                    $compression::write(&mut writer, messageReq)
                }

                do $compression::newReader(
                      &mut std::io::mem::BufReader::new(requestBytes),
                      capnp::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }

                {
                    let mut writer = std::io::mem::BufWriter::new(responseBytes);
                    $compression::write(&mut writer, messageRes)
                }

                do $compression::newReader(
                    &mut std::io::mem::BufReader::new(responseBytes),
                    capnp::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        fail!("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut outBuffered = capnp::io::BufferedOutputStream::new(&mut $output);
            let mut inBuffered = capnp::io::BufferedInputStream::new(&mut $input);
            for _ in range(0, $iters) {
                let mut messageRes = capnp::message::MessageBuilder::new_default();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                do $compression::newBufferedReader(
                    &mut inBuffered,
                    capnp::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }
                $compression::write(&mut outBuffered, messageRes);
                outBuffered.flush();
            }
        });
    )

macro_rules! syncClient(
    ( $testcase:ident, $compression:ident, $iters:expr) => ({
            let mut outStream = std::io::stdout();
            let mut outBuffered = capnp::io::BufferedOutputStream::new(&mut outStream);
            let mut inStream = std::io::stdin();
            let mut inBuffered = capnp::io::BufferedInputStream::new(&mut inStream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnp::message::MessageBuilder::new_default();
                let request = messageReq.initRoot::<$testcase::RequestBuilder>();

                let expected = $testcase::setupRequest(&mut rng, request);
                $compression::write(&mut outBuffered, messageReq);
                outBuffered.flush();

                do $compression::newBufferedReader(
                    &mut inBuffered,
                    capnp::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    assert!($testcase::checkResponse(responseReader, expected));
                }

            }
        });
    )

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

            // get a rustc crash if we put this inline below
            let io = ~[process::CreatePipe(true, false), // stdin
                       process::CreatePipe(false, true), // stdout
                       process::Ignored];

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

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

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

macro_rules! doTestcase(
    ( $testcase:ident, $mode:expr, $reuse:expr, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => passByObject!($testcase, $iters),
                ~"bytes" => passByBytes!($testcase, $compression, $iters),
                ~"client" => syncClient!($testcase, $compression, $iters),
                ~"server" => {
                    let mut input = std::io::stdin();
                    let mut output = std::io::stdout();
                    server!($testcase, $compression, $iters, input, output)
                }
                ~"pipe" => passByPipe!($testcase, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

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

pub fn main () {

    let args = std::os::args();

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

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

    // For now, just insist that re-use = none
    match args[3] {
        ~"no-reuse" => {}
        _ => fail!("for now, 'no-reuse' is the only allowed option for REUSE")
    }

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

}
<commit_msg>looks like that rustc was fixed<commit_after>/*
 * Copyright (c) 2013, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

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

#[link(name = "benchmark", vers = "alpha", author = "dwrensha")];

#[crate_type = "bin"];

extern mod capnp;

pub mod common;

pub mod carsales_capnp;
pub mod carsales;

pub mod catrank_capnp;
pub mod catrank;

pub mod eval_capnp;
pub mod eval;

mod Uncompressed {
    use capnp;
    use std;

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

    pub fn newReader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }

    pub fn newBufferedReader<R: std::io::Reader, T>(
        inputStream : &mut capnp::io::BufferedInputStream<R>,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(inputStream, options, cont)
    }
}

mod Packed {
    use capnp;
    use std;
    use capnp::serialize_packed::{WritePackedWrapper, WritePacked};

    pub fn write<T : std::io::Writer>(writer: &mut T,
                                          message: &capnp::message::MessageBuilder) {
        let mut w = WritePackedWrapper{writer: writer};
        w.writePackedMessage(message);
    }

    pub fn newReader<U : std::io::Reader, T>(
        inputStream : &mut U,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : &mut capnp::io::BufferedInputStream::new(inputStream)
            },
            options, cont)
    }

    pub fn newBufferedReader<R:std::io::Reader, T>(
        inputStream : &mut capnp::io::BufferedInputStream<R>,
        options : capnp::message::ReaderOptions,
        cont : &fn(v : &mut capnp::message::MessageReader) -> T) -> T {
        capnp::serialize::InputStreamMessageReader::new(
            &mut capnp::serialize_packed::PackedInputStream{
                inner : inputStream
            },
            options, cont)
    }

}

macro_rules! passByObject(
    ( $testcase:ident, $iters:expr ) => ({
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnp::message::MessageBuilder::new_default();
                let mut messageRes = capnp::message::MessageBuilder::new_default();

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

                do request.asReader |requestReader| {
                    $testcase::handleRequest(requestReader, response);
                }

                do response.asReader |responseReader| {
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        fail!("Incorrect response.");
                    }
                }
            }
        });
    )

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

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

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

                {
                    let mut writer = std::io::mem::BufWriter::new(requestBytes);
                    $compression::write(&mut writer, messageReq)
                }

                do $compression::newReader(
                      &mut std::io::mem::BufReader::new(requestBytes),
                      capnp::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }

                {
                    let mut writer = std::io::mem::BufWriter::new(responseBytes);
                    $compression::write(&mut writer, messageRes)
                }

                do $compression::newReader(
                    &mut std::io::mem::BufReader::new(responseBytes),
                    capnp::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    if (! $testcase::checkResponse(responseReader, expected)) {
                        fail!("Incorrect response.");
                    }
                }
            }
        });
    )

macro_rules! server(
    ( $testcase:ident, $compression:ident, $iters:expr, $input:expr, $output:expr) => ({
            let mut outBuffered = capnp::io::BufferedOutputStream::new(&mut $output);
            let mut inBuffered = capnp::io::BufferedInputStream::new(&mut $input);
            for _ in range(0, $iters) {
                let mut messageRes = capnp::message::MessageBuilder::new_default();
                let response = messageRes.initRoot::<$testcase::ResponseBuilder>();
                do $compression::newBufferedReader(
                    &mut inBuffered,
                    capnp::message::DEFAULT_READER_OPTIONS) |requestReader| {
                    let requestReader = $testcase::newRequestReader(requestReader.getRoot());
                    $testcase::handleRequest(requestReader, response);
                }
                $compression::write(&mut outBuffered, messageRes);
                outBuffered.flush();
            }
        });
    )

macro_rules! syncClient(
    ( $testcase:ident, $compression:ident, $iters:expr) => ({
            let mut outStream = std::io::stdout();
            let mut outBuffered = capnp::io::BufferedOutputStream::new(&mut outStream);
            let mut inStream = std::io::stdin();
            let mut inBuffered = capnp::io::BufferedInputStream::new(&mut inStream);
            let mut rng = common::FastRand::new();
            for _ in range(0, $iters) {
                let mut messageReq = capnp::message::MessageBuilder::new_default();
                let request = messageReq.initRoot::<$testcase::RequestBuilder>();

                let expected = $testcase::setupRequest(&mut rng, request);
                $compression::write(&mut outBuffered, messageReq);
                outBuffered.flush();

                do $compression::newBufferedReader(
                    &mut inBuffered,
                    capnp::message::DEFAULT_READER_OPTIONS) |responseReader| {
                    let responseReader = $testcase::newResponseReader(responseReader.getRoot());
                    assert!($testcase::checkResponse(responseReader, expected));
                }

            }
        });
    )

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

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

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

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

macro_rules! doTestcase(
    ( $testcase:ident, $mode:expr, $reuse:expr, $compression:ident, $iters:expr ) => ({
            match $mode {
                ~"object" => passByObject!($testcase, $iters),
                ~"bytes" => passByBytes!($testcase, $compression, $iters),
                ~"client" => syncClient!($testcase, $compression, $iters),
                ~"server" => {
                    let mut input = std::io::stdin();
                    let mut output = std::io::stdout();
                    server!($testcase, $compression, $iters, input, output)
                }
                ~"pipe" => passByPipe!($testcase, $compression, $iters),
                s => fail!("unrecognized mode: {}", s)
            }
        });
    )

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

pub fn main () {

    let args = std::os::args();

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

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

    // For now, just insist that re-use = none
    match args[3] {
        ~"no-reuse" => {}
        _ => fail!("for now, 'no-reuse' is the only allowed option for REUSE")
    }

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

}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::Write;
use std::str::FromStr;

use protocol::cmd::Cmd;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;

use super::errors::TcpTransportError;
use super::metrics::TransportMetrics;
use super::typedefs::TcpTransportResult;

pub struct TcpTransport<T> {
    stream: T,
    // queue up response data before writing to the stream
    outgoing_buffer: Vec<u8>,

    metrics: TransportMetrics,
    key_maxlen: u64,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard
            metrics: TransportMetrics::new(),
            outgoing_buffer: vec![],
            stream: stream,
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_max_line_len(&self) -> usize {
        // This needs to be the length of the longest command line, not
        // including data values for which the length is given upfront
        self.key_maxlen as usize + 100
    }

    pub fn get_metrics_clone(&self) -> TransportMetrics {
        self.metrics.clone()
    }

    pub fn get_stream(&self) -> &T {
        &self.stream
    }

    pub fn get_outgoing_buffer(&self) -> &Vec<u8> {
        &self.outgoing_buffer
    }

    // Basic bytes manipulation and reading from the stream

    pub fn as_string(&self, bytes: Vec<u8>) -> TcpTransportResult<String> {
        match String::from_utf8(bytes) {
            Ok(string) => Ok(string),
            Err(_) => Err(TcpTransportError::Utf8Error),
        }
    }

    pub fn as_number<N: FromStr>(&self,
                                 bytes: Vec<u8>)
                                 -> TcpTransportResult<N> {
        let string = try!(self.as_string(bytes));
        match string.parse::<N>() {
            Ok(num) => Ok(num),
            Err(_) => Err(TcpTransportError::NumberParseError),
        }
    }

    pub fn read_byte(&mut self) -> TcpTransportResult<u8> {
        let mut bytes = [0; 1];

        match self.stream.read(&mut bytes) {
            Ok(1) => {
                // Update metrics
                self.metrics.bytes_read += 1;

                Ok(bytes[0])
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn read_bytes(&mut self, len: u64) -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![];

        for _ in 0..len {
            let byte = try!(self.read_byte());
            bytes.push(byte);
        }

        Ok(bytes)
    }

    pub fn read_line(&mut self, maxlen: usize) -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![];
        let mut found_line_end = false;

        for _ in 0..maxlen {
            let byte = try!(self.read_byte());
            bytes.push(byte);

            // Look for \r\n
            if bytes.ends_with(&[13, 10]) {
                found_line_end = true;
                break;
            }
        }

        if found_line_end {
            // Pop off \r\n
            bytes.pop();
            bytes.pop();
            Ok(bytes)
        } else {
            Err(TcpTransportError::LineReadError)
        }
    }

    pub fn remove_first_char(&self,
                             bytes: &mut Vec<u8>)
                             -> TcpTransportResult<()> {
        match bytes.len() > 0 {
            true => {
                bytes.remove(0);
                Ok(())
            }
            false => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn parse_word(&self,
                      bytes: Vec<u8>)
                      -> TcpTransportResult<(Vec<u8>, Vec<u8>)> {
        let mut space_idx: i64 = -1;

        for i in 0..bytes.len() {
            // We're looking for a space
            if bytes[i] == 32 {
                space_idx = i as i64;
                break;
            }
        }

        if space_idx > -1 {
            let mut word = vec![];
            let mut rest = vec![];

            // TODO figure out how to return a modified vector instead of
            // copying the whole rest of it
            for i in 0..bytes.len() {
                let byte = bytes[i];
                if (i as i64) < space_idx {
                    word.push(byte);
                } else {
                    rest.push(byte);
                }
            }

            Ok((word, rest))

        } else {
            // If we've reached the end of the buffer without seeing a space
            // that makes the whole buffer a word
            Ok((bytes, vec![]))
        }
    }

    // Writing to the stream

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        for i in 0..bytes.len() {
            let byte = bytes[i];
            self.outgoing_buffer.push(byte);
        }

        Ok(bytes.len())
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        let rv = self.stream.write(&self.outgoing_buffer);
        self.outgoing_buffer.clear();

        if !rv.is_ok() {
            return Err(TcpTransportError::StreamWriteError);
        }

        let cnt_written = rv.unwrap();

        // Update metrics
        self.metrics.bytes_written += cnt_written as u64;

        let rv = self.stream.flush();

        match rv {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    // Parse individual commands

    pub fn parse_cmd_get(&self, mut rest: Vec<u8>) -> TcpTransportResult<Cmd> {
        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (key, rest) = try!(self.parse_word(rest));

        // We expect to find the end of the line now
        if rest.is_empty() {
            let key_str = try!(self.as_string(key));
            Ok(Cmd::Get(Get { key: key_str }))
        } else {
            Err(TcpTransportError::CommandParseError)
        }
    }

    pub fn parse_cmd_set(&mut self,
                         mut rest: Vec<u8>)
                         -> TcpTransportResult<Cmd> {
        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (key, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (flags, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (exptime, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (bytelen, _) = try!(self.parse_word(rest));

        let key_str = try!(self.as_string(key));
        let flags_num = try!(self.as_number::<u16>(flags));
        let exptime_num = try!(self.as_number::<u32>(exptime));
        let bytelen_num = try!(self.as_number::<u64>(bytelen));

        // We know the byte length, so now read the value
        let value = try!(self.read_bytes(bytelen_num));

        // Read the line termination marker
        let line_len = self.get_max_line_len();
        let rest = try!(self.read_line(line_len));

        // We got all the values we expected and there is nothing left
        if rest.is_empty() {
            return Ok(Cmd::Set(Set {
                key: key_str,
                exptime: exptime_num,
                data: value,
            }));
        }

        Err(TcpTransportError::CommandParseError)
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        let line_len = self.get_max_line_len();

        let fst_line = try!(self.read_line(line_len));
        let (keyword, rest) = try!(self.parse_word(fst_line));
        let keyword_str = try!(self.as_string(keyword));

        if keyword_str == "get" {
            return self.parse_cmd_get(rest);
        } else if keyword_str == "set" {
            return self.parse_cmd_set(rest);
        } else if keyword_str == "stats" {
            return Ok(Cmd::Stats);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Value(ref value) => {
                try!(self.write_string("VALUE ")); // keyword
                try!(self.write_string(&value.key)); // key
                try!(self.write_string(" 0 ")); // flags
                try!(self.write_string(&value.data.len().to_string())); // bytelen
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_bytes(&value.data)); // data block
                try!(self.write_string(&"\r\n".to_string())); // newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<commit_msg>fix transport output for stats<commit_after>use std::io::Read;
use std::io::Write;
use std::str::FromStr;

use protocol::cmd::Cmd;
use protocol::cmd::Get;
use protocol::cmd::Resp;
use protocol::cmd::Set;

use super::errors::TcpTransportError;
use super::metrics::TransportMetrics;
use super::typedefs::TcpTransportResult;

pub struct TcpTransport<T> {
    stream: T,
    // queue up response data before writing to the stream
    outgoing_buffer: Vec<u8>,

    metrics: TransportMetrics,
    key_maxlen: u64,
}

impl<T: Read + Write> TcpTransport<T> {
    pub fn new(stream: T) -> TcpTransport<T> {
        TcpTransport {
            key_maxlen: 250, // memcached standard
            metrics: TransportMetrics::new(),
            outgoing_buffer: vec![],
            stream: stream,
        }
    }

    pub fn with_key_maxlen(&mut self,
                           key_maxlen: u64)
                           -> &mut TcpTransport<T> {
        self.key_maxlen = key_maxlen;
        self
    }

    pub fn get_max_line_len(&self) -> usize {
        // This needs to be the length of the longest command line, not
        // including data values for which the length is given upfront
        self.key_maxlen as usize + 100
    }

    pub fn get_metrics_clone(&self) -> TransportMetrics {
        self.metrics.clone()
    }

    pub fn get_stream(&self) -> &T {
        &self.stream
    }

    pub fn get_outgoing_buffer(&self) -> &Vec<u8> {
        &self.outgoing_buffer
    }

    // Basic bytes manipulation and reading from the stream

    pub fn as_string(&self, bytes: Vec<u8>) -> TcpTransportResult<String> {
        match String::from_utf8(bytes) {
            Ok(string) => Ok(string),
            Err(_) => Err(TcpTransportError::Utf8Error),
        }
    }

    pub fn as_number<N: FromStr>(&self,
                                 bytes: Vec<u8>)
                                 -> TcpTransportResult<N> {
        let string = try!(self.as_string(bytes));
        match string.parse::<N>() {
            Ok(num) => Ok(num),
            Err(_) => Err(TcpTransportError::NumberParseError),
        }
    }

    pub fn read_byte(&mut self) -> TcpTransportResult<u8> {
        let mut bytes = [0; 1];

        match self.stream.read(&mut bytes) {
            Ok(1) => {
                // Update metrics
                self.metrics.bytes_read += 1;

                Ok(bytes[0])
            }
            _ => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn read_bytes(&mut self, len: u64) -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![];

        for _ in 0..len {
            let byte = try!(self.read_byte());
            bytes.push(byte);
        }

        Ok(bytes)
    }

    pub fn read_line(&mut self, maxlen: usize) -> TcpTransportResult<Vec<u8>> {
        let mut bytes = vec![];
        let mut found_line_end = false;

        for _ in 0..maxlen {
            let byte = try!(self.read_byte());
            bytes.push(byte);

            // Look for \r\n
            if bytes.ends_with(&[13, 10]) {
                found_line_end = true;
                break;
            }
        }

        if found_line_end {
            // Pop off \r\n
            bytes.pop();
            bytes.pop();
            Ok(bytes)
        } else {
            Err(TcpTransportError::LineReadError)
        }
    }

    pub fn remove_first_char(&self,
                             bytes: &mut Vec<u8>)
                             -> TcpTransportResult<()> {
        match bytes.len() > 0 {
            true => {
                bytes.remove(0);
                Ok(())
            }
            false => Err(TcpTransportError::StreamReadError),
        }
    }

    pub fn parse_word(&self,
                      bytes: Vec<u8>)
                      -> TcpTransportResult<(Vec<u8>, Vec<u8>)> {
        let mut space_idx: i64 = -1;

        for i in 0..bytes.len() {
            // We're looking for a space
            if bytes[i] == 32 {
                space_idx = i as i64;
                break;
            }
        }

        if space_idx > -1 {
            let mut word = vec![];
            let mut rest = vec![];

            // TODO figure out how to return a modified vector instead of
            // copying the whole rest of it
            for i in 0..bytes.len() {
                let byte = bytes[i];
                if (i as i64) < space_idx {
                    word.push(byte);
                } else {
                    rest.push(byte);
                }
            }

            Ok((word, rest))

        } else {
            // If we've reached the end of the buffer without seeing a space
            // that makes the whole buffer a word
            Ok((bytes, vec![]))
        }
    }

    // Writing to the stream

    pub fn write_bytes(&mut self,
                       bytes: &Vec<u8>)
                       -> TcpTransportResult<usize> {
        for i in 0..bytes.len() {
            let byte = bytes[i];
            self.outgoing_buffer.push(byte);
        }

        Ok(bytes.len())
    }

    pub fn write_string(&mut self, string: &str) -> TcpTransportResult<usize> {
        let bytes = string.to_string().into_bytes();
        Ok(try!(self.write_bytes(&bytes)))
    }

    pub fn flush_writes(&mut self) -> TcpTransportResult<()> {
        let rv = self.stream.write(&self.outgoing_buffer);
        self.outgoing_buffer.clear();

        if !rv.is_ok() {
            return Err(TcpTransportError::StreamWriteError);
        }

        let cnt_written = rv.unwrap();

        // Update metrics
        self.metrics.bytes_written += cnt_written as u64;

        let rv = self.stream.flush();

        match rv {
            Ok(_) => Ok(()),
            Err(_) => Err(TcpTransportError::StreamWriteError),
        }
    }

    // Parse individual commands

    pub fn parse_cmd_get(&self, mut rest: Vec<u8>) -> TcpTransportResult<Cmd> {
        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (key, rest) = try!(self.parse_word(rest));

        // We expect to find the end of the line now
        if rest.is_empty() {
            let key_str = try!(self.as_string(key));
            Ok(Cmd::Get(Get { key: key_str }))
        } else {
            Err(TcpTransportError::CommandParseError)
        }
    }

    pub fn parse_cmd_set(&mut self,
                         mut rest: Vec<u8>)
                         -> TcpTransportResult<Cmd> {
        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (key, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (flags, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (exptime, mut rest) = try!(self.parse_word(rest));

        try!(self.remove_first_char(&mut rest)); // remove leading space
        let (bytelen, _) = try!(self.parse_word(rest));

        let key_str = try!(self.as_string(key));
        let flags_num = try!(self.as_number::<u16>(flags));
        let exptime_num = try!(self.as_number::<u32>(exptime));
        let bytelen_num = try!(self.as_number::<u64>(bytelen));

        // We know the byte length, so now read the value
        let value = try!(self.read_bytes(bytelen_num));

        // Read the line termination marker
        let line_len = self.get_max_line_len();
        let rest = try!(self.read_line(line_len));

        // We got all the values we expected and there is nothing left
        if rest.is_empty() {
            return Ok(Cmd::Set(Set {
                key: key_str,
                exptime: exptime_num,
                data: value,
            }));
        }

        Err(TcpTransportError::CommandParseError)
    }

    // High level functions

    pub fn read_cmd(&mut self) -> TcpTransportResult<Cmd> {
        let line_len = self.get_max_line_len();

        let fst_line = try!(self.read_line(line_len));
        let (keyword, rest) = try!(self.parse_word(fst_line));
        let keyword_str = try!(self.as_string(keyword));

        if keyword_str == "get" {
            return self.parse_cmd_get(rest);
        } else if keyword_str == "set" {
            return self.parse_cmd_set(rest);
        } else if keyword_str == "stats" {
            return Ok(Cmd::Stats);
        }

        Err(TcpTransportError::InvalidCmd)
    }

    pub fn write_resp(&mut self, resp: &Resp) -> TcpTransportResult<()> {
        match *resp {
            Resp::Error => {
                try!(self.write_string("ERROR\r\n"));
            }
            Resp::Stats(ref stats) => {
                for stat in stats {
                    try!(self.write_string("STAT "));
                    try!(self.write_string(&stat.key));
                    try!(self.write_string(" "));
                    try!(self.write_string(&stat.value));
                    try!(self.write_string("\r\n"));
                }
                try!(self.write_string("END\r\n"));
            }
            Resp::Stored => {
                try!(self.write_string("STORED\r\n"));
            }
            Resp::Value(ref value) => {
                try!(self.write_string("VALUE ")); // keyword
                try!(self.write_string(&value.key)); // key
                try!(self.write_string(" 0 ")); // flags
                try!(self.write_string(&value.data.len().to_string())); // bytelen
                try!(self.write_string(&"\r\n".to_string())); // newline
                try!(self.write_bytes(&value.data)); // data block
                try!(self.write_string(&"\r\n".to_string())); // newline
            }
            _ => {
                return Err(TcpTransportError::StreamWriteError);
            }
        }

        // Make sure all bytes were actually sent
        self.flush_writes()
    }
}
<|endoftext|>