speculate 0.0.3

An RSpec inspired minimal testing framework for Rust.
use syntax::ast;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::ptr::P;

use block::{Block, Describe, It, Bench};

pub fn parse(parser: &mut Parser) -> Describe {
    parse_describe("sup", parser)
}

pub fn parse_describe(name: &str, parser: &mut Parser) -> Describe {
    let brace = token::CloseDelim(token::Brace);

    let mut before = None;
    let mut after = None;
    let mut blocks = vec![];

    loop {
        if parser.token == brace || parser.token == token::Eof {
            break
        }

        let span = parser.span;
        let ident = parser.parse_ident();

        match ident.as_str() {
            "describe" | "context" => {
                let (name, _) = parser.parse_str();
                parser.expect(&token::OpenDelim(token::Brace));
                let block = Block::Describe(parse_describe(name.get(), parser));
                parser.expect(&token::CloseDelim(token::Brace));
                blocks.push(block);
            },

            "it" => {
                let (name, _) = parser.parse_str();
                let block = parse_block(parser);

                blocks.push(Block::It(It {
                    name: name.get().to_string(),
                    block: block
                }))
            },

            "bench" => {
                let (name, _) = parser.parse_str();
                parser.expect(&token::BinOp(token::Or));
                let ident = parser.parse_ident();
                parser.expect(&token::BinOp(token::Or));
                let block = parse_block(parser);

                blocks.push(Block::Bench(Bench {
                    name: name.get().to_string(),
                    ident: ident,
                    block: block
                }))
            },

            "before" => {
                before = Some(parse_block(parser));
            },

            "after" => {
                after = Some(parse_block(parser));
            },

            otherwise => {
                let message = format!("Expected \
`describe`, `context`, `before`, `after`, or `it`, found `{}`", otherwise);
                parser.span_fatal(span, &*message)
            }
        }
    }

    Describe {
        name: name.to_string(),
        before: before,
        after: after,
        blocks: blocks
    }
}

fn parse_block(parser: &mut Parser) -> P<ast::Block> {
    let span = parser.span;
    let block = parser.parse_block();
    if block.expr.is_some() {
        parser.span_fatal(
            span,
            "last expression in this block must be terminated by `;`")
    }
    block
}