<commit_before>#![crate_name="incrust_plugin"]
#![crate_type="dylib"]
#![feature(quote, plugin_registrar, rustc_private)]

#[macro_use]
extern crate log;

extern crate syntax;
extern crate rustc;
extern crate rustc_plugin;

use rustc_plugin::Registry;

use std::fmt::Write;

use syntax::abi::Abi;
use syntax::ast::{self, DUMMY_NODE_ID};

use syntax::codemap::{Span, Spanned, dummy_spanned, respan, spanned, DUMMY_SP};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::print::pprust::{token_to_string, tts_to_string};
use syntax::tokenstream::TokenTree;
use syntax::util::small_vector::SmallVector;
use syntax::parse::{token, PResult};
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::ptr::P;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("emit_rust_template", emit_rust_template);
    reg.register_macro("emit_rust_and_js_template", emit_rust_and_js_template);
}

trait IntoViewItem {
    fn into_view_item<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Item>;
}

trait IntoBlock {
    fn into_block<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Block>;
}

trait IntoWriteStmt {
    fn into_write_stmt<'cx>(&self, ecx: &'cx ExtCtxt, w: ast::Ident) -> ast::Stmt;
}

trait IntoOutputActions {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction>;
}

struct View {
    name: String,
    span: Span,
    nodes: Vec<TemplateNode>
}

impl IntoViewItem for View {
    fn into_view_item<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Item> {
        create_view_item(ecx, self.span, &self)
    }
}

struct Element {
    element_type: String,
    span: Span,
    nodes: Vec<TemplateNode>
}

struct TemplateExpr {
    span: Span,
    tokens: Vec<TokenTree>
}

// Represents a parsed node in the template syntax
enum TemplateNode {
    ElementNode(Element),
    ExprNode(TemplateExpr)
}

// Represents a type of action to perform when rendering
enum OutputAction {
    // Text and computed values
    Write(String),
    WriteResult(TemplateExpr),

    // Elements
    WriteOpen(String),
    WriteClose(String),
    WriteVoid(String)

    //CallTemplate(String),
    //WriteElement(Element),
}

impl IntoWriteStmt for OutputAction {
    fn into_write_stmt<'cx>(&self, ecx: &'cx ExtCtxt, w: ast::Ident) -> ast::Stmt {
        match *self {
            OutputAction::Write(ref contents) => {
                //let w_name =  w.name.to_string();
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!($w, "{}", $contents);
                    }
                ).unwrap();

                stmt
            },

            // For now, write the expression as a string
            OutputAction::WriteResult(ref template_expr) => {
                let contents = tts_to_string(&template_expr.tokens);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteOpen(ref element_type) => {
                let contents = format!("<{}>", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteClose(ref element_type) => {
                let contents = format!("</{}>", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteVoid(ref element_type) => {
                let contents = format!("<{} />", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            }
        }
    }
}

impl IntoOutputActions for Element {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        let nodes = &self.nodes;
        let element_type = &self.element_type;
        let mut output_actions = Vec::new();

        output_actions.push(OutputAction::WriteOpen(element_type.clone()));

        let child_actions : Vec<OutputAction> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .collect();
        output_actions.extend(child_actions);

        output_actions.push(OutputAction::WriteClose(element_type.clone()));

        output_actions
    }
}

impl IntoOutputActions for TemplateExpr {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        // For now, output the element as a token string
        let contents = tts_to_string(&self.tokens);
        vec![OutputAction::Write(contents)]
    }
}

impl IntoOutputActions for TemplateNode {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        match self {
            &TemplateNode::ElementNode(ref element) => element.into_output_actions(ecx),
            &TemplateNode::ExprNode(ref template_expr) => template_expr.into_output_actions(ecx)
        }
    }
}

impl IntoOutputActions for View {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        let name = &self.name;
        let nodes = &self.nodes;

        let w_ident = ecx.ident_of("out");
        let mut stmts = Vec::new();

        let out_stmt = quote_stmt!(ecx, let mut $w_ident = String::new()).unwrap();
        stmts.push(out_stmt);

        let output_actions: Vec<OutputAction> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .collect();

        output_actions
    }
}

impl IntoBlock for View {
    fn into_block<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Block> {
        let name = &self.name;
        let nodes = &self.nodes;

        let w_ident = ecx.ident_of("out");
        let mut stmts = Vec::new();

        let out_stmt = quote_stmt!(ecx, let mut $w_ident = String::new()).unwrap();
        stmts.push(out_stmt);

        let write_stmts: Vec<ast::Stmt> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .map(|output_action| output_action.into_write_stmt(ecx, w_ident))
            .collect();
        stmts.extend(write_stmts);

        // Return rendered string for now
        stmts.push(quote_stmt!(ecx, $w_ident).unwrap());

        ecx.block(self.span, stmts)
    }
}

fn parse_element<'cx, 'a>(ecx: &'cx ExtCtxt, mut parser: &mut Parser<'a>, span: Span, element_type: String) -> PResult<'a, Element> {
    try!(parser.expect(&token::OpenDelim(token::Bracket)));

    let nodes = try!(parse_contents(ecx, &mut parser, span));

    Ok(Element { element_type: element_type, span: span, nodes: nodes })
}

fn parse_template_expr<'a>(parser: &mut Parser<'a>, span: Span) -> PResult<'a, TemplateExpr> {
    try!(parser.expect(&token::OpenDelim(token::Brace)));

    let tokens = parser.parse_seq_to_end(
        &token::CloseDelim(token::Brace),
        SeqSep::none(),
        |parser| parser.parse_token_tree())
        .unwrap();

    Ok(TemplateExpr { span: span, tokens: tokens })
}

fn parse_node<'cx, 'a>(ecx: &'cx ExtCtxt, mut parser: &mut Parser<'a>, span: Span) -> PResult<'a, TemplateNode> {
    let is_open = parser.token == token::OpenDelim(token::Brace);

    if is_open {
        // Template expression
        let template_expr = try!(parse_template_expr(&mut parser, DUMMY_SP));
        Ok(TemplateNode::ExprNode(template_expr))
    } else {
        ecx.span_warn(span, "Expecting nested element");

        // Nested element
        let element_type = try!(parser.parse_ident());
        let element = try!(parse_element(ecx, &mut parser, DUMMY_SP, element_type.name.to_string()));

        Ok(TemplateNode::ElementNode(element))
    }
}

fn parse_contents<'cx, 'a>(ecx: &'cx ExtCtxt, parser: &mut Parser<'a>, span: Span) -> PResult<'a, Vec<TemplateNode>> {
    let mut nodes : Vec<TemplateNode> = Vec::new();

    loop {
        match parser.token {
            token::CloseDelim(token::Bracket) => {
                ecx.span_warn(span, "Got close contents");
                break;
            },

            _ => {
                ecx.span_warn(span, "Parsing node");
                let node = try!(parse_node(ecx, parser, DUMMY_SP));
                nodes.push(node);
            }
        }
    }

    Ok(nodes)
}

fn parse_view<'cx, 'a>(ecx: &'cx ExtCtxt, parser: &mut Parser<'a>, span: Span) -> PResult<'a, View> {
    let view_token = parser.parse_ident().unwrap();
    let view_name = parser.parse_ident().unwrap();

    try!(parser.expect(&token::OpenDelim(token::Bracket)));

    let nodes = try!(parse_contents(ecx, parser, span));

    Ok(View { name: view_name.name.to_string(), span: span, nodes: nodes })
}

fn create_view_item<'cx>(ecx: &'cx ExtCtxt, span: Span, view: &View) -> P<ast::Item> {
    let name = ecx.ident_of(&format!("rusttemplate_view_{}", view.name));
    let block = view.into_block(ecx);

    let inputs = vec![];
    let ret_ty = quote_ty!(ecx, String);
    ecx.item_fn(DUMMY_SP, name, inputs, ret_ty, block)
}

fn create_template_block<'cx>(ecx: &'cx ExtCtxt, span: Span, views: Vec<View>) -> Box<MacResult + 'cx> {
    let view_item_stmts: Vec<ast::Stmt> = views.iter()
        .map(|view| view.into_view_item(ecx))
        .map(|item| ecx.stmt_item(span, item))
    .collect();

    let mut stmts = Vec::new();
    stmts.extend(view_item_stmts);

    let name = ecx.ident_of("rusttemplate_view_root");
    let args = vec![];
    let call_expr = ecx.expr_call_ident(span, name, args);
    stmts.push(ecx.stmt_expr(call_expr));

    let block = ecx.block(span, stmts);

    MacEager::expr(ecx.expr_block(block))
}

fn parse_js_out_var<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>) -> PResult<'a, P<ast::Expr>> {
    // Read js variable expression
    let js_ident = try!(parser.parse_expr());
    // Consume ,
    try!(parser.expect(&token::Comma));

    Ok(js_ident)
}

fn parse_into_output_actions<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>) -> PResult<'a, Vec<OutputAction>> {
    match parse_view(ecx, parser, DUMMY_SP) {
        Ok(view) => {
            let output_actions = view.into_output_actions(ecx);
            Ok(output_actions)
        },

        Err(mut err) => {
            err.emit();
            Err(err)
        }
    }
}

fn construct_js_function(view_name: String, output_actions: Vec<OutputAction>) -> &'static str {
    ""
}

fn parse_emit_rust_template<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>, js_ident: Option<P<ast::Expr>>) -> PResult<'a, Box<MacResult + 'cx>> {
    let view = try!(parse_view(ecx, parser, DUMMY_SP));
    let views = vec![view];

    Ok(create_template_block(ecx, DUMMY_SP, views))
}

fn parse_emit_js_and_rust_template<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>) -> PResult<'a, Box<MacResult + 'cx>> {
    let js_ident = try!(parse_js_out_var(ecx, parser));
    parse_emit_rust_template(ecx, parser, Some(js_ident))
}

fn emit_rust_template<'cx>(
        ecx: &'cx mut ExtCtxt,
        span: Span,
        tts: &[TokenTree]) -> Box<MacResult + 'cx> {

    let mut parser = ecx.new_parser_from_tts(tts);
    match parse_emit_rust_template(ecx, &mut parser, None) {
        Err(mut err) => { err.emit(); return DummyResult::expr(DUMMY_SP); },
        Ok(result) => result
    }
}

fn emit_rust_and_js_template<'cx>(
        ecx: &'cx mut ExtCtxt,
        span: Span,
        tts: &[TokenTree]) -> Box<MacResult + 'cx> {

    let mut parser = ecx.new_parser_from_tts(tts);
    match parse_emit_js_and_rust_template(ecx, &mut parser) {
        Err(mut err) => { err.emit(); return DummyResult::expr(DUMMY_SP); },
        Ok(result) => result
    }
}<commit_msg>Remove unused method introduced in refactoring<commit_after>#![crate_name="incrust_plugin"]
#![crate_type="dylib"]
#![feature(quote, plugin_registrar, rustc_private)]

#[macro_use]
extern crate log;

extern crate syntax;
extern crate rustc;
extern crate rustc_plugin;

use rustc_plugin::Registry;

use std::fmt::Write;

use syntax::abi::Abi;
use syntax::ast::{self, DUMMY_NODE_ID};

use syntax::codemap::{Span, Spanned, dummy_spanned, respan, spanned, DUMMY_SP};
use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ToTokens;
use syntax::print::pprust::{token_to_string, tts_to_string};
use syntax::tokenstream::TokenTree;
use syntax::util::small_vector::SmallVector;
use syntax::parse::{token, PResult};
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::ptr::P;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("emit_rust_template", emit_rust_template);
    reg.register_macro("emit_rust_and_js_template", emit_rust_and_js_template);
}

trait IntoViewItem {
    fn into_view_item<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Item>;
}

trait IntoBlock {
    fn into_block<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Block>;
}

trait IntoWriteStmt {
    fn into_write_stmt<'cx>(&self, ecx: &'cx ExtCtxt, w: ast::Ident) -> ast::Stmt;
}

trait IntoOutputActions {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction>;
}

struct View {
    name: String,
    span: Span,
    nodes: Vec<TemplateNode>
}

impl IntoViewItem for View {
    fn into_view_item<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Item> {
        create_view_item(ecx, self.span, &self)
    }
}

struct Element {
    element_type: String,
    span: Span,
    nodes: Vec<TemplateNode>
}

struct TemplateExpr {
    span: Span,
    tokens: Vec<TokenTree>
}

// Represents a parsed node in the template syntax
enum TemplateNode {
    ElementNode(Element),
    ExprNode(TemplateExpr)
}

// Represents a type of action to perform when rendering
enum OutputAction {
    // Text and computed values
    Write(String),
    WriteResult(TemplateExpr),

    // Elements
    WriteOpen(String),
    WriteClose(String),
    WriteVoid(String)

    //CallTemplate(String),
    //WriteElement(Element),
}

impl IntoWriteStmt for OutputAction {
    fn into_write_stmt<'cx>(&self, ecx: &'cx ExtCtxt, w: ast::Ident) -> ast::Stmt {
        match *self {
            OutputAction::Write(ref contents) => {
                //let w_name =  w.name.to_string();
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!($w, "{}", $contents);
                    }
                ).unwrap();

                stmt
            },

            // For now, write the expression as a string
            OutputAction::WriteResult(ref template_expr) => {
                let contents = tts_to_string(&template_expr.tokens);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteOpen(ref element_type) => {
                let contents = format!("<{}>", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteClose(ref element_type) => {
                let contents = format!("</{}>", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            },

            OutputAction::WriteVoid(ref element_type) => {
                let contents = format!("<{} />", element_type);
                let stmt = quote_stmt!(ecx,
                    {
                        println!("Writing contents [{}] to ${}", $contents, "out");
                        write!(out, $contents);
                    }
                ).unwrap();

                stmt
            }
        }
    }
}

impl IntoOutputActions for Element {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        let nodes = &self.nodes;
        let element_type = &self.element_type;
        let mut output_actions = Vec::new();

        output_actions.push(OutputAction::WriteOpen(element_type.clone()));

        let child_actions : Vec<OutputAction> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .collect();
        output_actions.extend(child_actions);

        output_actions.push(OutputAction::WriteClose(element_type.clone()));

        output_actions
    }
}

impl IntoOutputActions for TemplateExpr {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        // For now, output the element as a token string
        let contents = tts_to_string(&self.tokens);
        vec![OutputAction::Write(contents)]
    }
}

impl IntoOutputActions for TemplateNode {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        match self {
            &TemplateNode::ElementNode(ref element) => element.into_output_actions(ecx),
            &TemplateNode::ExprNode(ref template_expr) => template_expr.into_output_actions(ecx)
        }
    }
}

impl IntoOutputActions for View {
    fn into_output_actions<'cx>(&self, ecx: &'cx ExtCtxt) -> Vec<OutputAction> {
        let name = &self.name;
        let nodes = &self.nodes;

        let w_ident = ecx.ident_of("out");
        let mut stmts = Vec::new();

        let out_stmt = quote_stmt!(ecx, let mut $w_ident = String::new()).unwrap();
        stmts.push(out_stmt);

        let output_actions: Vec<OutputAction> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .collect();

        output_actions
    }
}

impl IntoBlock for View {
    fn into_block<'cx>(&self, ecx: &'cx ExtCtxt) -> P<ast::Block> {
        let name = &self.name;
        let nodes = &self.nodes;

        let w_ident = ecx.ident_of("out");
        let mut stmts = Vec::new();

        let out_stmt = quote_stmt!(ecx, let mut $w_ident = String::new()).unwrap();
        stmts.push(out_stmt);

        let write_stmts: Vec<ast::Stmt> = nodes.iter()
            .flat_map(|node| node.into_output_actions(ecx))
            .map(|output_action| output_action.into_write_stmt(ecx, w_ident))
            .collect();
        stmts.extend(write_stmts);

        // Return rendered string for now
        stmts.push(quote_stmt!(ecx, $w_ident).unwrap());

        ecx.block(self.span, stmts)
    }
}

fn parse_element<'cx, 'a>(ecx: &'cx ExtCtxt, mut parser: &mut Parser<'a>, span: Span, element_type: String) -> PResult<'a, Element> {
    try!(parser.expect(&token::OpenDelim(token::Bracket)));

    let nodes = try!(parse_contents(ecx, &mut parser, span));

    Ok(Element { element_type: element_type, span: span, nodes: nodes })
}

fn parse_template_expr<'a>(parser: &mut Parser<'a>, span: Span) -> PResult<'a, TemplateExpr> {
    try!(parser.expect(&token::OpenDelim(token::Brace)));

    let tokens = parser.parse_seq_to_end(
        &token::CloseDelim(token::Brace),
        SeqSep::none(),
        |parser| parser.parse_token_tree())
        .unwrap();

    Ok(TemplateExpr { span: span, tokens: tokens })
}

fn parse_node<'cx, 'a>(ecx: &'cx ExtCtxt, mut parser: &mut Parser<'a>, span: Span) -> PResult<'a, TemplateNode> {
    let is_open = parser.token == token::OpenDelim(token::Brace);

    if is_open {
        // Template expression
        let template_expr = try!(parse_template_expr(&mut parser, DUMMY_SP));
        Ok(TemplateNode::ExprNode(template_expr))
    } else {
        ecx.span_warn(span, "Expecting nested element");

        // Nested element
        let element_type = try!(parser.parse_ident());
        let element = try!(parse_element(ecx, &mut parser, DUMMY_SP, element_type.name.to_string()));

        Ok(TemplateNode::ElementNode(element))
    }
}

fn parse_contents<'cx, 'a>(ecx: &'cx ExtCtxt, parser: &mut Parser<'a>, span: Span) -> PResult<'a, Vec<TemplateNode>> {
    let mut nodes : Vec<TemplateNode> = Vec::new();

    loop {
        match parser.token {
            token::CloseDelim(token::Bracket) => {
                ecx.span_warn(span, "Got close contents");
                break;
            },

            _ => {
                ecx.span_warn(span, "Parsing node");
                let node = try!(parse_node(ecx, parser, DUMMY_SP));
                nodes.push(node);
            }
        }
    }

    Ok(nodes)
}

fn parse_view<'cx, 'a>(ecx: &'cx ExtCtxt, parser: &mut Parser<'a>, span: Span) -> PResult<'a, View> {
    let view_token = parser.parse_ident().unwrap();
    let view_name = parser.parse_ident().unwrap();

    try!(parser.expect(&token::OpenDelim(token::Bracket)));

    let nodes = try!(parse_contents(ecx, parser, span));

    Ok(View { name: view_name.name.to_string(), span: span, nodes: nodes })
}

fn create_view_item<'cx>(ecx: &'cx ExtCtxt, span: Span, view: &View) -> P<ast::Item> {
    let name = ecx.ident_of(&format!("rusttemplate_view_{}", view.name));
    let block = view.into_block(ecx);

    let inputs = vec![];
    let ret_ty = quote_ty!(ecx, String);
    ecx.item_fn(DUMMY_SP, name, inputs, ret_ty, block)
}

fn create_template_block<'cx>(ecx: &'cx ExtCtxt, span: Span, views: Vec<View>) -> Box<MacResult + 'cx> {
    let view_item_stmts: Vec<ast::Stmt> = views.iter()
        .map(|view| view.into_view_item(ecx))
        .map(|item| ecx.stmt_item(span, item))
    .collect();

    let mut stmts = Vec::new();
    stmts.extend(view_item_stmts);

    let name = ecx.ident_of("rusttemplate_view_root");
    let args = vec![];
    let call_expr = ecx.expr_call_ident(span, name, args);
    stmts.push(ecx.stmt_expr(call_expr));

    let block = ecx.block(span, stmts);

    MacEager::expr(ecx.expr_block(block))
}

fn parse_js_out_var<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>) -> PResult<'a, P<ast::Expr>> {
    // Read js variable expression
    let js_ident = try!(parser.parse_expr());
    // Consume ,
    try!(parser.expect(&token::Comma));

    Ok(js_ident)
}

fn construct_js_function(view_name: String, output_actions: Vec<OutputAction>) -> &'static str {
    ""
}

fn parse_emit_rust_template<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>, js_ident: Option<P<ast::Expr>>) -> PResult<'a, Box<MacResult + 'cx>> {
    let view = try!(parse_view(ecx, parser, DUMMY_SP));
    let views = vec![view];

    Ok(create_template_block(ecx, DUMMY_SP, views))
}

fn parse_emit_js_and_rust_template<'cx, 'a>(ecx: &'cx mut ExtCtxt, parser: &mut Parser<'a>) -> PResult<'a, Box<MacResult + 'cx>> {
    let js_ident = try!(parse_js_out_var(ecx, parser));
    parse_emit_rust_template(ecx, parser, Some(js_ident))
}

fn emit_rust_template<'cx>(
        ecx: &'cx mut ExtCtxt,
        span: Span,
        tts: &[TokenTree]) -> Box<MacResult + 'cx> {

    let mut parser = ecx.new_parser_from_tts(tts);
    match parse_emit_rust_template(ecx, &mut parser, None) {
        Err(mut err) => { err.emit(); return DummyResult::expr(DUMMY_SP); },
        Ok(result) => result
    }
}

fn emit_rust_and_js_template<'cx>(
        ecx: &'cx mut ExtCtxt,
        span: Span,
        tts: &[TokenTree]) -> Box<MacResult + 'cx> {

    let mut parser = ecx.new_parser_from_tts(tts);
    match parse_emit_js_and_rust_template(ecx, &mut parser) {
        Err(mut err) => { err.emit(); return DummyResult::expr(DUMMY_SP); },
        Ok(result) => result
    }
}<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture { texture: JS<WebGLTexture>, level: i32 },
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: JS::from_ref(texture),
                    level: level }
                );

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<commit_msg>webgl: Don't forget to update FBO status after detaching a texture.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLFramebufferBinding;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::js::{HeapGCValue, JS, Root};
use dom::bindings::reflector::reflect_dom_object;
use dom::globalscope::GlobalScope;
use dom::webglobject::WebGLObject;
use dom::webglrenderbuffer::WebGLRenderbuffer;
use dom::webgltexture::WebGLTexture;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use webrender_traits::{WebGLCommand, WebGLFramebufferBindingRequest, WebGLFramebufferId, WebGLResult, WebGLError};

#[must_root]
#[derive(JSTraceable, Clone, HeapSizeOf)]
enum WebGLFramebufferAttachment {
    Renderbuffer(JS<WebGLRenderbuffer>),
    Texture { texture: JS<WebGLTexture>, level: i32 },
}

impl HeapGCValue for WebGLFramebufferAttachment {}

#[dom_struct]
pub struct WebGLFramebuffer {
    webgl_object: WebGLObject,
    id: WebGLFramebufferId,
    /// target can only be gl::FRAMEBUFFER at the moment
    target: Cell<Option<u32>>,
    is_deleted: Cell<bool>,
    status: Cell<u32>,
    #[ignore_heap_size_of = "Defined in ipc-channel"]
    renderer: IpcSender<CanvasMsg>,

    // The attachment points for textures and renderbuffers on this
    // FBO.
    color: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depth: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    stencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
    depthstencil: DOMRefCell<Option<WebGLFramebufferAttachment>>,
}

impl WebGLFramebuffer {
    fn new_inherited(renderer: IpcSender<CanvasMsg>,
                     id: WebGLFramebufferId)
                     -> WebGLFramebuffer {
        WebGLFramebuffer {
            webgl_object: WebGLObject::new_inherited(),
            id: id,
            target: Cell::new(None),
            is_deleted: Cell::new(false),
            renderer: renderer,
            status: Cell::new(constants::FRAMEBUFFER_UNSUPPORTED),
            color: DOMRefCell::new(None),
            depth: DOMRefCell::new(None),
            stencil: DOMRefCell::new(None),
            depthstencil: DOMRefCell::new(None),
        }
    }

    pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>)
                     -> Option<Root<WebGLFramebuffer>> {
        let (sender, receiver) = ipc::channel().unwrap();
        renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap();

        let result = receiver.recv().unwrap();
        result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id))
    }

    pub fn new(global: &GlobalScope,
               renderer: IpcSender<CanvasMsg>,
               id: WebGLFramebufferId)
               -> Root<WebGLFramebuffer> {
        reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id),
                           global,
                           WebGLFramebufferBinding::Wrap)
    }
}

impl WebGLFramebuffer {
    pub fn id(&self) -> WebGLFramebufferId {
        self.id
    }

    pub fn bind(&self, target: u32) {
        // Update the framebuffer status on binding.  It may have
        // changed if its attachments were resized or deleted while
        // we've been unbound.
        self.update_status();

        self.target.set(Some(target));
        let cmd = WebGLCommand::BindFramebuffer(target, WebGLFramebufferBindingRequest::Explicit(self.id));
        self.renderer.send(CanvasMsg::WebGL(cmd)).unwrap();
    }

    pub fn delete(&self) {
        if !self.is_deleted.get() {
            self.is_deleted.set(true);
            let _ = self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DeleteFramebuffer(self.id)));
        }
    }

    pub fn is_deleted(&self) -> bool {
        self.is_deleted.get()
    }

    fn update_status(&self) {
        let has_c = self.color.borrow().is_some();
        let has_z = self.depth.borrow().is_some();
        let has_s = self.stencil.borrow().is_some();
        let has_zs = self.depthstencil.borrow().is_some();

        // From the WebGL spec, 6.6 ("Framebuffer Object Attachments"):
        //
        //    "In the WebGL API, it is an error to concurrently attach
        //     renderbuffers to the following combinations of
        //     attachment points:
        //
        //     DEPTH_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     STENCIL_ATTACHMENT + DEPTH_STENCIL_ATTACHMENT
        //     DEPTH_ATTACHMENT + STENCIL_ATTACHMENT
        //
        //     If any of the constraints above are violated, then:
        //
        //     checkFramebufferStatus must return FRAMEBUFFER_UNSUPPORTED."
        if (has_zs && (has_z || has_s)) ||
            (has_z && has_s) {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
            return;
        }

        if has_c || has_z || has_zs || has_s {
            self.status.set(constants::FRAMEBUFFER_COMPLETE);
        } else {
            self.status.set(constants::FRAMEBUFFER_UNSUPPORTED);
        }
    }

    pub fn check_status(&self) -> u32 {
        return self.status.get();
    }

    pub fn renderbuffer(&self, attachment: u32, rb: Option<&WebGLRenderbuffer>) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let rb_id = match rb {
            Some(rb) => {
                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Renderbuffer(JS::from_ref(rb)));
                Some(rb.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferRenderbuffer(constants::FRAMEBUFFER,
                                                                                attachment,
                                                                                constants::RENDERBUFFER,
                                                                                rb_id))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn texture2d(&self, attachment: u32, textarget: u32, texture: Option<&WebGLTexture>,
                     level: i32) -> WebGLResult<()> {
        let binding = match attachment {
            constants::COLOR_ATTACHMENT0 => &self.color,
            constants::DEPTH_ATTACHMENT => &self.depth,
            constants::STENCIL_ATTACHMENT => &self.stencil,
            constants::DEPTH_STENCIL_ATTACHMENT => &self.depthstencil,
            _ => return Err(WebGLError::InvalidEnum),
        };

        let tex_id = match texture {
            // Note, from the GLES 2.0.25 spec, page 113:
            //      "If texture is zero, then textarget and level are ignored."
            Some(texture) => {
                // From the GLES 2.0.25 spec, page 113:
                //
                //     "level specifies the mipmap level of the texture image
                //      to be attached to the framebuffer and must be
                //      0. Otherwise, INVALID_VALUE is generated."
                if level != 0 {
                    return Err(WebGLError::InvalidValue);
                }

                //     "If texture is not zero, then texture must either
                //      name an existing texture object with an target of
                //      textarget, or texture must name an existing cube
                //      map texture and textarget must be one of:
                //      TEXTURE_CUBE_MAP_POSITIVE_X,
                //      TEXTURE_CUBE_MAP_POSITIVE_Y,
                //      TEXTURE_CUBE_MAP_POSITIVE_Z,
                //      TEXTURE_CUBE_MAP_NEGATIVE_X,
                //      TEXTURE_CUBE_MAP_NEGATIVE_Y, or
                //      TEXTURE_CUBE_MAP_NEGATIVE_Z. Otherwise,
                //      INVALID_OPERATION is generated."
                let is_cube = match textarget {
                    constants::TEXTURE_2D => false,

                    constants::TEXTURE_CUBE_MAP_POSITIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_POSITIVE_Z => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_X => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Y => true,
                    constants::TEXTURE_CUBE_MAP_NEGATIVE_Z => true,

                    _ => return Err(WebGLError::InvalidEnum),
                };

                match texture.target() {
                    Some(constants::TEXTURE_CUBE_MAP) if is_cube => {}
                    Some(_) if !is_cube => {}
                    _ => return Err(WebGLError::InvalidOperation),
                }

                *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture {
                    texture: JS::from_ref(texture),
                    level: level }
                );

                Some(texture.id())
            }

            _ => {
                *binding.borrow_mut() = None;
                self.update_status();
                None
            }
        };

        self.renderer.send(CanvasMsg::WebGL(WebGLCommand::FramebufferTexture2D(constants::FRAMEBUFFER,
                                                                               attachment,
                                                                               textarget,
                                                                               tex_id,
                                                                               level))).unwrap();

        self.update_status();
        Ok(())
    }

    pub fn detach_renderbuffer(&self, rb: &WebGLRenderbuffer) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Renderbuffer(ref att_rb))
                        if rb.id() == att_rb.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn detach_texture(&self, texture: &WebGLTexture) {
        let attachments = [&self.color,
                           &self.depth,
                           &self.stencil,
                           &self.depthstencil];

        for attachment in &attachments {
            let matched = {
                match *attachment.borrow() {
                    Some(WebGLFramebufferAttachment::Texture { texture: ref att_texture, .. })
                        if texture.id() == att_texture.id() => true,
                    _ => false,
                }
            };

            if matched {
                *attachment.borrow_mut() = None;
                self.update_status();
            }
        }
    }

    pub fn target(&self) -> Option<u32> {
        self.target.get()
    }
}

impl Drop for WebGLFramebuffer {
    fn drop(&mut self) {
        self.delete();
    }
}
<|endoftext|>


<commit_before>/// Quasi-quotation macro that accepts input like the [`quote!`] macro but uses
/// type inference to figure out a return type for those tokens.
///
/// [`quote!`]: https://docs.rs/quote/0.6/quote/index.html
///
/// The return type can be any syntax tree node that implements the [`Synom`]
/// trait.
///
/// [`Synom`]: synom/trait.Synom.html
///
/// ```
/// #[macro_use]
/// extern crate syn;
///
/// #[macro_use]
/// extern crate quote;
///
/// use syn::Stmt;
///
/// fn main() {
///     let name = quote!(v);
///     let ty = quote!(u8);
///
///     let stmt: Stmt = parse_quote! {
///         let #name: #ty = Default::default();
///     };
///
///     println!("{:#?}", stmt);
/// }
/// ```
///
/// *This macro is available if Syn is built with the `"parsing"` feature,
/// although interpolation of syntax tree nodes into the quoted tokens is only
/// supported if Syn is built with the `"printing"` feature as well.*
///
/// # Example
///
/// The following helper function adds a bound `T: HeapSize` to every type
/// parameter `T` in the input generics.
///
/// ```
/// # #[macro_use]
/// # extern crate syn;
/// #
/// # #[macro_use]
/// # extern crate quote;
/// #
/// # use syn::{Generics, GenericParam};
/// #
/// // Add a bound `T: HeapSize` to every type parameter T.
/// fn add_trait_bounds(mut generics: Generics) -> Generics {
///     for param in &mut generics.params {
///         if let GenericParam::Type(ref mut type_param) = *param {
///             type_param.bounds.push(parse_quote!(HeapSize));
///         }
///     }
///     generics
/// }
/// #
/// # fn main() {}
/// ```
///
/// # Special cases
///
/// This macro can parse the following additional types as a special case even
/// though they do not implement the `Synom` trait.
///
/// - [`Attribute`] — parses one attribute, allowing either outer like `#[...]`
///   or inner like `#![...]`
/// - [`Punctuated<T, P>`] — parses zero or more `T` separated by punctuation
///   `P` with optional trailing punctuation
///
/// [`Attribute`]: struct.Attribute.html
/// [`Punctuated<T, P>`]: punctuated/struct.Punctuated.html
///
/// # Panics
///
/// Panics if the tokens fail to parse as the expected syntax tree type. The
/// caller is responsible for ensuring that the input tokens are syntactically
/// valid.
#[macro_export]
macro_rules! parse_quote {
    ($($tt:tt)*) => {
        $crate::parse_quote::parse($crate::export::From::from(quote!($($tt)*)))
    };
}

////////////////////////////////////////////////////////////////////////////////
// Can parse any type that implements Synom.

use parse::{Parse, ParseStream, Parser, Result};
use proc_macro2::TokenStream;

// Not public API.
#[doc(hidden)]
pub fn parse<T: ParseQuote>(token_stream: TokenStream) -> T {
    let parser = T::parse;
    match parser.parse2(token_stream) {
        Ok(t) => t,
        Err(err) => panic!("{}", err),
    }
}

// Not public API.
#[doc(hidden)]
pub trait ParseQuote: Sized {
    fn parse(input: ParseStream) -> Result<Self>;
}

impl<T: Parse> ParseQuote for T {
    fn parse(input: ParseStream) -> Result<Self> {
        <T as Parse>::parse(input)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Any other types that we want `parse_quote!` to be able to parse.

#[cfg(any(feature = "full", feature = "derive"))]
use {attr, Attribute};

#[cfg(any(feature = "full", feature = "derive"))]
impl ParseQuote for Attribute {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(Token![#]) && input.peek2(Token![!]) {
            attr::parsing::single_parse_inner(input)
        } else {
            attr::parsing::single_parse_outer(input)
        }
    }
}
<commit_msg>Fix references to synom in parse_quote documentation<commit_after>/// Quasi-quotation macro that accepts input like the [`quote!`] macro but uses
/// type inference to figure out a return type for those tokens.
///
/// [`quote!`]: https://docs.rs/quote/0.6/quote/index.html
///
/// The return type can be any syntax tree node that implements the [`Parse`]
/// trait.
///
/// [`Parse`]: parse/trait.Parse.html
///
/// ```
/// #[macro_use]
/// extern crate syn;
///
/// #[macro_use]
/// extern crate quote;
///
/// use syn::Stmt;
///
/// fn main() {
///     let name = quote!(v);
///     let ty = quote!(u8);
///
///     let stmt: Stmt = parse_quote! {
///         let #name: #ty = Default::default();
///     };
///
///     println!("{:#?}", stmt);
/// }
/// ```
///
/// *This macro is available if Syn is built with the `"parsing"` feature,
/// although interpolation of syntax tree nodes into the quoted tokens is only
/// supported if Syn is built with the `"printing"` feature as well.*
///
/// # Example
///
/// The following helper function adds a bound `T: HeapSize` to every type
/// parameter `T` in the input generics.
///
/// ```
/// # #[macro_use]
/// # extern crate syn;
/// #
/// # #[macro_use]
/// # extern crate quote;
/// #
/// # use syn::{Generics, GenericParam};
/// #
/// // Add a bound `T: HeapSize` to every type parameter T.
/// fn add_trait_bounds(mut generics: Generics) -> Generics {
///     for param in &mut generics.params {
///         if let GenericParam::Type(ref mut type_param) = *param {
///             type_param.bounds.push(parse_quote!(HeapSize));
///         }
///     }
///     generics
/// }
/// #
/// # fn main() {}
/// ```
///
/// # Special cases
///
/// This macro can parse the following additional types as a special case even
/// though they do not implement the `Parse` trait.
///
/// - [`Attribute`] — parses one attribute, allowing either outer like `#[...]`
///   or inner like `#![...]`
/// - [`Punctuated<T, P>`] — parses zero or more `T` separated by punctuation
///   `P` with optional trailing punctuation
///
/// [`Attribute`]: struct.Attribute.html
/// [`Punctuated<T, P>`]: punctuated/struct.Punctuated.html
///
/// # Panics
///
/// Panics if the tokens fail to parse as the expected syntax tree type. The
/// caller is responsible for ensuring that the input tokens are syntactically
/// valid.
#[macro_export]
macro_rules! parse_quote {
    ($($tt:tt)*) => {
        $crate::parse_quote::parse($crate::export::From::from(quote!($($tt)*)))
    };
}

////////////////////////////////////////////////////////////////////////////////
// Can parse any type that implements Synom.

use parse::{Parse, ParseStream, Parser, Result};
use proc_macro2::TokenStream;

// Not public API.
#[doc(hidden)]
pub fn parse<T: ParseQuote>(token_stream: TokenStream) -> T {
    let parser = T::parse;
    match parser.parse2(token_stream) {
        Ok(t) => t,
        Err(err) => panic!("{}", err),
    }
}

// Not public API.
#[doc(hidden)]
pub trait ParseQuote: Sized {
    fn parse(input: ParseStream) -> Result<Self>;
}

impl<T: Parse> ParseQuote for T {
    fn parse(input: ParseStream) -> Result<Self> {
        <T as Parse>::parse(input)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Any other types that we want `parse_quote!` to be able to parse.

#[cfg(any(feature = "full", feature = "derive"))]
use {attr, Attribute};

#[cfg(any(feature = "full", feature = "derive"))]
impl ParseQuote for Attribute {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.peek(Token![#]) && input.peek2(Token![!]) {
            attr::parsing::single_parse_inner(input)
        } else {
            attr::parsing::single_parse_outer(input)
        }
    }
}
<|endoftext|>


<commit_before>extern crate htmlescape;

use std::time::Duration;

use irc::client::prelude::*;

use regex::Regex;

use plugin::*;
use utils::Url;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;
use failure::ResultExt;

lazy_static! {
    static ref URL_RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
    static ref WORD_RE: Regex = Regex::new(r"(\w+)").unwrap();
}

#[derive(PluginName, Debug)]
pub struct UrlTitles {
    max_kib: usize,
}

#[derive(Clone, Debug)]
struct Title(String, Option<usize>);

impl From<String> for Title {
    fn from(title: String) -> Self {
        Title(title, None)
    }
}

impl From<Title> for String {
    fn from(title: Title) -> Self {
        title.0
    }
}

impl Title {
    fn find_by_delimiters(body: &str, delimiters: [&str; 3]) -> Result<Self, UrlError> {
        let title = body.find(delimiters[0])
            .map(|tag| {
                body[tag..]
                    .find(delimiters[1])
                    .map(|offset| tag + offset + delimiters[1].len())
                    .map(|start| {
                        body[start..]
                            .find(delimiters[2])
                            .map(|offset| start + offset)
                            .map(|end| &body[start..end])
                    })
            })
            .and_then(|s| s.and_then(|s| s))
            .ok_or(ErrorKind::MissingTitle)?;

        debug!("Found title {:?} with delimiters {:?}", title, delimiters);

        htmlescape::decode_html(title)
            .map(|t| t.into())
            .map_err(|_| ErrorKind::HtmlDecoding.into())
    }

    fn find_ogtitle<'a>(body: &str) -> Result<Self, UrlError> {
        Self::find_by_delimiters(body, ["property=\"og:title\"", "content=\"", "\""])
    }

    fn find_title<'a>(body: &str) -> Result<Self, UrlError> {
        Self::find_by_delimiters(body, ["<title", ">", "</title>"])
    }

    // TODO Improve logic
    fn get_usefulness(self, url: &str) -> Self {
        let mut usefulness = 0;
        for word in WORD_RE.find_iter(&self.0) {
            let w = word.as_str().to_lowercase();
            if w.len() > 2 && !url.to_lowercase().contains(&w) {
                usefulness += 1;
            }
        }

        Title(self.0, Some(usefulness))
    }

    pub fn usefulness(&self) -> usize {
        self.1.expect("Usefulness should be calculated already")
    }

    fn clean_up(self) -> Self {
        Title(self.0.trim().replace('\n', "|").replace('\r', "|"), self.1)
    }

    pub fn find_clean_ogtitle<'a>(body: &str, url: &str) -> Result<Self, UrlError> {
        let title = Self::find_ogtitle(body)?;
        Ok(title.get_usefulness(url).clean_up())
    }

    pub fn find_clean_title<'a>(body: &str, url: &str) -> Result<Self, UrlError> {
        let title = Self::find_title(body)?;
        Ok(title.get_usefulness(url).clean_up())
    }
}

impl UrlTitles {
    /// If a file is larger than `max_kib` KiB the download is stopped
    pub fn new(max_kib: usize) -> Self {
        UrlTitles { max_kib: max_kib }
    }

    fn grep_url<'a>(&self, msg: &'a str) -> Option<Url<'a>> {
        let captures = URL_RE.captures(msg)?;
        debug!("Url captures: {:?}", captures);

        Some(captures.get(2)?.as_str().into())
    }

    fn url(&self, text: &str) -> Result<String, UrlError> {
        let url = self.grep_url(text)
            .ok_or(ErrorKind::MissingUrl)?
            .max_kib(self.max_kib)
            .timeout(Duration::from_secs(5));
        let body = url.request().context(ErrorKind::Download)?;

        let title = Title::find_clean_title(&body, url.as_str());
        let og_title = Title::find_clean_ogtitle(&body, url.as_str());

        let title = match (title, og_title) {
            (Ok(title), Ok(og_title)) => {
                if title.usefulness() > og_title.usefulness() {
                    title
                } else {
                    og_title
                }
            }
            (Ok(title), _) => title,
            (_, Ok(title)) => title,
            (Err(e), _) => Err(e)?,
        };

        if title.usefulness() > 1 {
            Ok(title.into())
        } else {
            Err(ErrorKind::UselessTitle.into())
        }
    }
}

impl Plugin for UrlTitles {
    fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref msg) => if URL_RE.is_match(msg) {
                ExecutionStatus::RequiresThread
            } else {
                ExecutionStatus::Done
            },
            _ => ExecutionStatus::Done,
        }
    }

    fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
        match message.command {
            Command::PRIVMSG(_, ref content) => match self.url(content) {
                Ok(title) => client
                    .send_privmsg(message.response_target().unwrap(), &title)
                    .context(FrippyErrorKind::Connection)?,
                Err(e) => Err(e).context(FrippyErrorKind::Url)?,
            },
            _ => (),
        }

        Ok(())
    }

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        Ok(client
            .send_notice(
                &command.source,
                "This Plugin does not implement any commands.",
            )
            .context(FrippyErrorKind::Connection)?)
    }

    fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
        self.url(&command.tokens[0])
            .map_err(|e| e.cause().unwrap().to_string())
    }
}

pub mod error {
    /// A URL plugin error
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "UrlError"]
    pub enum ErrorKind {
        /// A download error
        #[fail(display = "A download error occured")]
        Download,

        /// Missing URL error
        #[fail(display = "No URL was found")]
        MissingUrl,

        /// Missing title error
        #[fail(display = "No title was found")]
        MissingTitle,

        /// Useless title error
        #[fail(display = "The titles found were not useful enough")]
        UselessTitle,

        /// Html decoding error
        #[fail(display = "Failed to decode Html characters")]
        HtmlDecoding,
    }
}
<commit_msg>Url: Add prefix to messages to show context<commit_after>extern crate htmlescape;

use std::time::Duration;

use irc::client::prelude::*;

use regex::Regex;

use plugin::*;
use utils::Url;

use self::error::*;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use failure::Fail;
use failure::ResultExt;

lazy_static! {
    static ref URL_RE: Regex = Regex::new(r"(^|\s)(https?://\S+)").unwrap();
    static ref WORD_RE: Regex = Regex::new(r"(\w+)").unwrap();
}

#[derive(PluginName, Debug)]
pub struct UrlTitles {
    max_kib: usize,
}

#[derive(Clone, Debug)]
struct Title(String, Option<usize>);

impl From<String> for Title {
    fn from(title: String) -> Self {
        Title(title, None)
    }
}

impl From<Title> for String {
    fn from(title: Title) -> Self {
        title.0
    }
}

impl Title {
    fn find_by_delimiters(body: &str, delimiters: [&str; 3]) -> Result<Self, UrlError> {
        let title = body.find(delimiters[0])
            .map(|tag| {
                body[tag..]
                    .find(delimiters[1])
                    .map(|offset| tag + offset + delimiters[1].len())
                    .map(|start| {
                        body[start..]
                            .find(delimiters[2])
                            .map(|offset| start + offset)
                            .map(|end| &body[start..end])
                    })
            })
            .and_then(|s| s.and_then(|s| s))
            .ok_or(ErrorKind::MissingTitle)?;

        debug!("Found title {:?} with delimiters {:?}", title, delimiters);

        htmlescape::decode_html(title)
            .map(|t| t.into())
            .map_err(|_| ErrorKind::HtmlDecoding.into())
    }

    fn find_ogtitle<'a>(body: &str) -> Result<Self, UrlError> {
        Self::find_by_delimiters(body, ["property=\"og:title\"", "content=\"", "\""])
    }

    fn find_title<'a>(body: &str) -> Result<Self, UrlError> {
        Self::find_by_delimiters(body, ["<title", ">", "</title>"])
    }

    // TODO Improve logic
    fn get_usefulness(self, url: &str) -> Self {
        let mut usefulness = 0;
        for word in WORD_RE.find_iter(&self.0) {
            let w = word.as_str().to_lowercase();
            if w.len() > 2 && !url.to_lowercase().contains(&w) {
                usefulness += 1;
            }
        }

        Title(self.0, Some(usefulness))
    }

    pub fn usefulness(&self) -> usize {
        self.1.expect("Usefulness should be calculated already")
    }

    fn clean_up(self) -> Self {
        Title(self.0.trim().replace('\n', "|").replace('\r', "|"), self.1)
    }

    pub fn find_clean_ogtitle<'a>(body: &str, url: &str) -> Result<Self, UrlError> {
        let title = Self::find_ogtitle(body)?;
        Ok(title.get_usefulness(url).clean_up())
    }

    pub fn find_clean_title<'a>(body: &str, url: &str) -> Result<Self, UrlError> {
        let title = Self::find_title(body)?;
        Ok(title.get_usefulness(url).clean_up())
    }
}

impl UrlTitles {
    /// If a file is larger than `max_kib` KiB the download is stopped
    pub fn new(max_kib: usize) -> Self {
        UrlTitles { max_kib: max_kib }
    }

    fn grep_url<'a>(&self, msg: &'a str) -> Option<Url<'a>> {
        let captures = URL_RE.captures(msg)?;
        debug!("Url captures: {:?}", captures);

        Some(captures.get(2)?.as_str().into())
    }

    fn url(&self, text: &str) -> Result<String, UrlError> {
        let url = self.grep_url(text)
            .ok_or(ErrorKind::MissingUrl)?
            .max_kib(self.max_kib)
            .timeout(Duration::from_secs(5));
        let body = url.request().context(ErrorKind::Download)?;

        let title = Title::find_clean_title(&body, url.as_str());
        let og_title = Title::find_clean_ogtitle(&body, url.as_str());

        let title = match (title, og_title) {
            (Ok(title), Ok(og_title)) => {
                if title.usefulness() > og_title.usefulness() {
                    title
                } else {
                    og_title
                }
            }
            (Ok(title), _) => title,
            (_, Ok(title)) => title,
            (Err(e), _) => Err(e)?,
        };

        if title.usefulness() > 1 {
            Ok(title.into())
        } else {
            Err(ErrorKind::UselessTitle.into())
        }
    }
}

impl Plugin for UrlTitles {
    fn execute(&self, _: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref msg) => if URL_RE.is_match(msg) {
                ExecutionStatus::RequiresThread
            } else {
                ExecutionStatus::Done
            },
            _ => ExecutionStatus::Done,
        }
    }

    fn execute_threaded(&self, client: &IrcClient, message: &Message) -> Result<(), FrippyError> {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                let title = self.url(content).context(FrippyErrorKind::Url)?;
                let response = format!("[URL] {}", title);

                client
                    .send_privmsg(message.response_target().unwrap(), &response)
                    .context(FrippyErrorKind::Connection)?;
            }
            _ => (),
        }

        Ok(())
    }

    fn command(&self, client: &IrcClient, command: PluginCommand) -> Result<(), FrippyError> {
        Ok(client
            .send_notice(
                &command.source,
                "This Plugin does not implement any commands.",
            )
            .context(FrippyErrorKind::Connection)?)
    }

    fn evaluate(&self, _: &IrcClient, command: PluginCommand) -> Result<String, String> {
        self.url(&command.tokens[0])
            .map_err(|e| e.cause().unwrap().to_string())
    }
}

pub mod error {
    /// A URL plugin error
    #[derive(Copy, Clone, Eq, PartialEq, Debug, Fail, Error)]
    #[error = "UrlError"]
    pub enum ErrorKind {
        /// A download error
        #[fail(display = "A download error occured")]
        Download,

        /// Missing URL error
        #[fail(display = "No URL was found")]
        MissingUrl,

        /// Missing title error
        #[fail(display = "No title was found")]
        MissingTitle,

        /// Useless title error
        #[fail(display = "The titles found were not useful enough")]
        UselessTitle,

        /// Html decoding error
        #[fail(display = "Failed to decode Html characters")]
        HtmlDecoding,
    }
}
<|endoftext|>