<commit_before>use super::*;
use super::NumNode::*;
use super::ExprNode::*;

use svm::cell::Atom::*;
use svm::cell::Inst::*;
use svm::cell::SVMCell::*;

#[test]
fn test_compile_add() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_sub() {
    let ast = SExprNode {
        operator: NameNode { name: "-".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB)
        ])
    )
}

#[test]
fn test_compile_div() {
    let ast = SExprNode {
        operator: NameNode { name: "/".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
            NumConst(IntConst(IntNode{ value: 3isize })),
            NumConst(IntConst(IntNode{ value: 4isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(4isize)),
            InstCell(LDC), AtomCell(SInt(3isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(1isize)),
            InstCell(DIV)

        ])
    )
}

#[test]
fn test_compile_mul() {
    let ast = SExprNode {
        operator: NameNode { name: "*".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(FloatConst(FloatNode{ value: 2f64 })),
            NumConst(FloatConst(FloatNode{ value: 3f64 })),
            NumConst(FloatConst(FloatNode{ value: 4f64 }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(Float(4f64)),
            InstCell(LDC), AtomCell(Float(3f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(2f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(MUL)
        ])
    )
}

#[test]
fn test_compile_nested_sexpr() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 4isize })),
            SExpr(SExprNode {
                operator: NameNode { name: "-".to_string() },
                operands: vec![
                    NumConst(IntConst(IntNode{ value: 1isize })),
                    NumConst(IntConst(IntNode{ value: 2isize })),
                    NumConst(IntConst(IntNode{ value: 3isize }))
                ]
            })
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(3)),
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(4)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_gte() {
    let ast = SExprNode {
        operator: NameNode { name: ">=".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(GTE)
        ])
    )
}

#[test]
fn test_compile_lte() {
    let ast = SExprNode {
        operator: NameNode { name: "<=".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 3usize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(UInt(3usize)),
            InstCell(LTE)
        ])
    )
}

<commit_msg>Add tests for String.compile()<commit_after>use super::*;
use super::NumNode::*;
use super::ExprNode::*;

use svm::cell::Atom::*;
use svm::cell::Inst::*;
use svm::cell::SVMCell::*;

use svm::slist::List::{Cons,Nil};

#[test]
fn test_compile_add() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_sub() {
    let ast = SExprNode {
        operator: NameNode { name: "-".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize })),
            NumConst(UIntConst(UIntNode{ value: 9usize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(UInt(9)),
            InstCell(SUB)
        ])
    )
}

#[test]
fn test_compile_div() {
    let ast = SExprNode {
        operator: NameNode { name: "/".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 1isize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
            NumConst(IntConst(IntNode{ value: 3isize })),
            NumConst(IntConst(IntNode{ value: 4isize }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(4isize)),
            InstCell(LDC), AtomCell(SInt(3isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(DIV),
            InstCell(LDC), AtomCell(SInt(1isize)),
            InstCell(DIV)

        ])
    )
}

#[test]
fn test_compile_mul() {
    let ast = SExprNode {
        operator: NameNode { name: "*".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(FloatConst(FloatNode{ value: 2f64 })),
            NumConst(FloatConst(FloatNode{ value: 3f64 })),
            NumConst(FloatConst(FloatNode{ value: 4f64 }))
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(Float(4f64)),
            InstCell(LDC), AtomCell(Float(3f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(2f64)),
            InstCell(MUL),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(MUL)
        ])
    )
}

#[test]
fn test_compile_nested_sexpr() {
    let ast = SExprNode {
        operator: NameNode { name: "+".to_string() },
        operands: vec![
            NumConst(IntConst(IntNode{ value: 4isize })),
            SExpr(SExprNode {
                operator: NameNode { name: "-".to_string() },
                operands: vec![
                    NumConst(IntConst(IntNode{ value: 1isize })),
                    NumConst(IntConst(IntNode{ value: 2isize })),
                    NumConst(IntConst(IntNode{ value: 3isize }))
                ]
            })
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(3)),
            InstCell(LDC), AtomCell(SInt(2)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(1)),
            InstCell(SUB),
            InstCell(LDC), AtomCell(SInt(4)),
            InstCell(ADD)
        ])
    )
}

#[test]
fn test_compile_gte() {
    let ast = SExprNode {
        operator: NameNode { name: ">=".to_string() },
        operands: vec![
            NumConst(FloatConst(FloatNode{ value: 1f64 })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(Float(1f64)),
            InstCell(GTE)
        ])
    )
}

#[test]
fn test_compile_lte() {
    let ast = SExprNode {
        operator: NameNode { name: "<=".to_string() },
        operands: vec![
            NumConst(UIntConst(UIntNode{ value: 3usize })),
            NumConst(IntConst(IntNode{ value: 2isize })),
        ]
    };
    assert_eq!(
        ast.compile(&SymTable::new()),
        Ok(vec![
            InstCell(LDC), AtomCell(SInt(2isize)),
            InstCell(LDC), AtomCell(UInt(3usize)),
            InstCell(LTE)
        ])
    )
}

#[test]
fn test_compile_string() {
    assert_eq!(
        StringNode{ value: "a string".to_string() }
            .compile(&SymTable::new()),
        Ok(vec![ListCell(box list!(
            AtomCell(Char('a')),
            AtomCell(Char(' ')),
            AtomCell(Char('s')),
            AtomCell(Char('t')),
            AtomCell(Char('r')),
            AtomCell(Char('i')),
            AtomCell(Char('n')),
            AtomCell(Char('g'))
            ))])
        )
}

<|endoftext|>


<commit_before>extern crate rust_playpen;

#[macro_use] extern crate iron;
extern crate hyper;
extern crate staticfile;
extern crate router;
extern crate unicase;
extern crate rustc_serialize;

use rust_playpen::*;

use iron::prelude::*;
use iron::status;
use iron::headers;
use iron::middleware::AfterMiddleware;
use iron::modifiers::Header;
use iron::method::Method;
use hyper::header;
use staticfile::Static;
use router::Router;
use unicase::UniCase;
use rustc_serialize::json;

use std::fmt;
use std::path::Path;
use std::io::Read;
use std::process::Command;

#[derive(Clone, Debug)]
struct XXssProtection(bool);

impl header::Header for XXssProtection {
    fn header_name() -> &'static str {
        "X-XSS-Protection"
    }

    fn parse_header(raw: &[Vec<u8>]) -> hyper::Result<Self> {
        if raw.len() == 1 {
            let line = &raw[0];
            if line.len() == 1 {
                let byte = line[0];
                match byte {
                    b'1' => return Ok(XXssProtection(true)),
                    b'0' => return Ok(XXssProtection(false)),
                    _ => ()
                }
            }
        }
        Err(hyper::Error::Header)
    }
}

impl header::HeaderFormat for XXssProtection {
    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.0 {
            f.write_str("1")
        } else {
            f.write_str("0")
        }
    }
}

fn index(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((Path::new("static/web.html"),
                       Header(XXssProtection(false)))))
}

/// The JSON-encoded request sent to `evaluate.json`.
#[derive(RustcDecodable)]
struct EvaluateReq {
    color: Option<bool>,
    test: Option<bool>,
    version: Option<String>,
    optimize: Option<String>,
    separate_output: Option<bool>,
    code: String,
}

fn evaluate(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    itry!(req.body.read_to_string(&mut body));

    let data: EvaluateReq = itry!(json::decode(&body));
    let color = data.color.unwrap_or(false);
    let test = data.test.unwrap_or(false);
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
    let opt = itry!(data.optimize.map(|opt| opt.parse()).unwrap_or(Ok(OptLevel::O2)));
    let separate_output = data.separate_output.unwrap_or(false);

    let mut args = vec![String::from("-C"), format!("opt-level={}", opt.as_u8())];
    if opt == OptLevel::O0 {
        args.push(String::from("-g"));
    }
    if color {
        args.push(String::from("--color=always"));
    }
    if test {
        args.push(String::from("--test"));
    }

    let (_status, output) = itry!(
        rust_playpen::exec(version, "/usr/local/bin/evaluate.sh", &args, data.code));

    if separate_output {
        // {"rustc": "...", "program": "..."}
        let mut split = output.splitn(2, |b| *b == b'\xff');
        let rustc = String::from_utf8(split.next().unwrap().into()).unwrap();

        let mut obj = json::Object::new();
        obj.insert(String::from("rustc"), json::Json::String(rustc));

        if let Some(program) = split.next() {
            // Compilation succeeded
            let output = String::from_utf8_lossy(program).into_owned();
            obj.insert(String::from("program"), json::Json::String(output));
        }

        Ok(Response::with((status::Ok, format!("{}", json::Json::Object(obj)))))
    } else {
        // {"result": "..."}
        // TODO
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
struct CompileReq {
    syntax: Option<String>,
    color: Option<bool>,
    version: Option<String>,
    optimize: Option<String>,
    emit: Option<String>,
    code: String,
}

fn compile(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    itry!(req.body.read_to_string(&mut body));

    let data: CompileReq = itry!(json::decode(&body));
    let syntax = data.syntax.map(|s| s.parse().unwrap()).unwrap_or(AsmFlavor::Att);
    let color = data.color.unwrap_or(false);
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
    let opt = itry!(data.optimize.map(|opt| opt.parse()).unwrap_or(Ok(OptLevel::O2)));
    let emit = itry!(data.emit.map(|emit| emit.parse()).unwrap_or(Ok(CompileOutput::Asm)));

    let mut args = vec![
        String::from("-C"),
        format!("opt-level={}", opt.as_u8()),
        String::from("-C"),
        format!("llvm-args=-x86-asm-syntax={}", syntax.as_str()),
        String::from("--crate-type=lib"),
    ];
    for opt in emit.as_opts() {
        args.push(String::from(*opt));
    }
    if opt == OptLevel::O0 {
        args.push(String::from("-g"));
    }
    if color {
        args.push(String::from("--color=always"));
    }

    let (_status, output) = itry!(
        rust_playpen::exec(version, "/usr/local/bin/compile.sh", &args, data.code));
    let mut split = output.splitn(2, |b| *b == b'\xff');
    let rustc = String::from_utf8(split.next().unwrap().into()).unwrap();

    let mut obj = json::Object::new();;
    match split.next() {
        Some(program_out) => {
            // Compilation succeeded
            let output = highlight(emit,
                                   &String::from_utf8_lossy(program_out).into_owned());
            obj.insert(String::from("result"), json::Json::String(output));
        }
        None => {
            obj.insert(String::from("error"), json::Json::String(rustc));
        }
    }

    Ok(Response::with((status::Ok, format!("{}", json::Json::Object(obj)))))
}

#[derive(RustcDecodable)]
struct FormatReq {
    version: Option<String>,
    code: String,
}

fn format(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    req.body.read_to_string(&mut body).unwrap();

    let data: FormatReq = itry!(json::decode(&body));
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));

    let (status, output) = itry!(rust_playpen::exec(version, "/usr/bin/rustfmt", &[], data.code));
    let output = String::from_utf8(output).unwrap();
    let mut response_obj = json::Object::new();
    if status.success() {
        response_obj.insert(String::from("result"), json::Json::String(output));
    } else {
        response_obj.insert(String::from("error"), json::Json::String(output));
    }

    Ok(Response::with((status::Ok, format!("{}", json::Json::Object(response_obj)))))
}

// This is neat!
struct EnablePostCors;
impl AfterMiddleware for EnablePostCors {
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(res.set(Header(headers::AccessControlAllowOrigin::Any))
              .set(Header(headers::AccessControlAllowMethods(
                  vec![Method::Post,
                       Method::Options])))
              .set(Header(headers::AccessControlAllowHeaders(
                  vec![UniCase(String::from("Origin")),
                       UniCase(String::from("Accept")),
                       UniCase(String::from("Content-Type"))]))))
    }
}

fn main() {
    // Make sure pygmentize is installed before starting the server
    Command::new("pygmentize").spawn().unwrap().kill().unwrap();

    let mut router = Router::new();
    router.get("/", index);
    router.get("/:path", Static::new("static"));
    router.post("/evaluate.json", evaluate);
    router.post("/compile.json", compile);
    router.post("/format.json", format);

    // Use our router as the middleware, and pass the generated response through `EnablePostCors`
    let mut chain = Chain::new(router);
    chain.link_after(EnablePostCors);

    let addr = ("0.0.0.0", 80);
    println!("listening on {:?}", addr);
    Iron::new(chain).http(addr).unwrap();
}
<commit_msg>Don't set crate-type=lib<commit_after>extern crate rust_playpen;

#[macro_use] extern crate iron;
extern crate hyper;
extern crate staticfile;
extern crate router;
extern crate unicase;
extern crate rustc_serialize;

use rust_playpen::*;

use iron::prelude::*;
use iron::status;
use iron::headers;
use iron::middleware::AfterMiddleware;
use iron::modifiers::Header;
use iron::method::Method;
use hyper::header;
use staticfile::Static;
use router::Router;
use unicase::UniCase;
use rustc_serialize::json;

use std::fmt;
use std::path::Path;
use std::io::Read;
use std::process::Command;

#[derive(Clone, Debug)]
struct XXssProtection(bool);

impl header::Header for XXssProtection {
    fn header_name() -> &'static str {
        "X-XSS-Protection"
    }

    fn parse_header(raw: &[Vec<u8>]) -> hyper::Result<Self> {
        if raw.len() == 1 {
            let line = &raw[0];
            if line.len() == 1 {
                let byte = line[0];
                match byte {
                    b'1' => return Ok(XXssProtection(true)),
                    b'0' => return Ok(XXssProtection(false)),
                    _ => ()
                }
            }
        }
        Err(hyper::Error::Header)
    }
}

impl header::HeaderFormat for XXssProtection {
    fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.0 {
            f.write_str("1")
        } else {
            f.write_str("0")
        }
    }
}

fn index(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((Path::new("static/web.html"),
                       Header(XXssProtection(false)))))
}

/// The JSON-encoded request sent to `evaluate.json`.
#[derive(RustcDecodable)]
struct EvaluateReq {
    color: Option<bool>,
    test: Option<bool>,
    version: Option<String>,
    optimize: Option<String>,
    separate_output: Option<bool>,
    code: String,
}

fn evaluate(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    itry!(req.body.read_to_string(&mut body));

    let data: EvaluateReq = itry!(json::decode(&body));
    let color = data.color.unwrap_or(false);
    let test = data.test.unwrap_or(false);
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
    let opt = itry!(data.optimize.map(|opt| opt.parse()).unwrap_or(Ok(OptLevel::O2)));
    let separate_output = data.separate_output.unwrap_or(false);

    let mut args = vec![String::from("-C"), format!("opt-level={}", opt.as_u8())];
    if opt == OptLevel::O0 {
        args.push(String::from("-g"));
    }
    if color {
        args.push(String::from("--color=always"));
    }
    if test {
        args.push(String::from("--test"));
    }

    let (_status, output) = itry!(
        rust_playpen::exec(version, "/usr/local/bin/evaluate.sh", &args, data.code));

    if separate_output {
        // {"rustc": "...", "program": "..."}
        let mut split = output.splitn(2, |b| *b == b'\xff');
        let rustc = String::from_utf8(split.next().unwrap().into()).unwrap();

        let mut obj = json::Object::new();
        obj.insert(String::from("rustc"), json::Json::String(rustc));

        if let Some(program) = split.next() {
            // Compilation succeeded
            let output = String::from_utf8_lossy(program).into_owned();
            obj.insert(String::from("program"), json::Json::String(output));
        }

        Ok(Response::with((status::Ok, format!("{}", json::Json::Object(obj)))))
    } else {
        // {"result": "..."}
        // TODO
        unimplemented!()
    }
}

#[derive(RustcDecodable)]
struct CompileReq {
    syntax: Option<String>,
    color: Option<bool>,
    version: Option<String>,
    optimize: Option<String>,
    emit: Option<String>,
    code: String,
}

fn compile(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    itry!(req.body.read_to_string(&mut body));

    let data: CompileReq = itry!(json::decode(&body));
    let syntax = data.syntax.map(|s| s.parse().unwrap()).unwrap_or(AsmFlavor::Att);
    let color = data.color.unwrap_or(false);
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
    let opt = itry!(data.optimize.map(|opt| opt.parse()).unwrap_or(Ok(OptLevel::O2)));
    let emit = itry!(data.emit.map(|emit| emit.parse()).unwrap_or(Ok(CompileOutput::Asm)));

    let mut args = vec![
        String::from("-C"),
        format!("opt-level={}", opt.as_u8()),
        String::from("-C"),
        format!("llvm-args=-x86-asm-syntax={}", syntax.as_str()),
    ];
    for opt in emit.as_opts() {
        args.push(String::from(*opt));
    }
    if opt == OptLevel::O0 {
        args.push(String::from("-g"));
    }
    if color {
        args.push(String::from("--color=always"));
    }

    let (_status, output) = itry!(
        rust_playpen::exec(version, "/usr/local/bin/compile.sh", &args, data.code));
    let mut split = output.splitn(2, |b| *b == b'\xff');
    let rustc = String::from_utf8(split.next().unwrap().into()).unwrap();

    let mut obj = json::Object::new();;
    match split.next() {
        Some(program_out) => {
            // Compilation succeeded
            let output = highlight(emit,
                                   &String::from_utf8_lossy(program_out).into_owned());
            obj.insert(String::from("result"), json::Json::String(output));
        }
        None => {
            obj.insert(String::from("error"), json::Json::String(rustc));
        }
    }

    Ok(Response::with((status::Ok, format!("{}", json::Json::Object(obj)))))
}

#[derive(RustcDecodable)]
struct FormatReq {
    version: Option<String>,
    code: String,
}

fn format(req: &mut Request) -> IronResult<Response> {
    let mut body = String::new();
    req.body.read_to_string(&mut body).unwrap();

    let data: FormatReq = itry!(json::decode(&body));
    let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));

    let (status, output) = itry!(rust_playpen::exec(version, "/usr/bin/rustfmt", &[], data.code));
    let output = String::from_utf8(output).unwrap();
    let mut response_obj = json::Object::new();
    if status.success() {
        response_obj.insert(String::from("result"), json::Json::String(output));
    } else {
        response_obj.insert(String::from("error"), json::Json::String(output));
    }

    Ok(Response::with((status::Ok, format!("{}", json::Json::Object(response_obj)))))
}

// This is neat!
struct EnablePostCors;
impl AfterMiddleware for EnablePostCors {
    fn after(&self, _: &mut Request, res: Response) -> IronResult<Response> {
        Ok(res.set(Header(headers::AccessControlAllowOrigin::Any))
              .set(Header(headers::AccessControlAllowMethods(
                  vec![Method::Post,
                       Method::Options])))
              .set(Header(headers::AccessControlAllowHeaders(
                  vec![UniCase(String::from("Origin")),
                       UniCase(String::from("Accept")),
                       UniCase(String::from("Content-Type"))]))))
    }
}

fn main() {
    // Make sure pygmentize is installed before starting the server
    Command::new("pygmentize").spawn().unwrap().kill().unwrap();

    let mut router = Router::new();
    router.get("/", index);
    router.get("/:path", Static::new("static"));
    router.post("/evaluate.json", evaluate);
    router.post("/compile.json", compile);
    router.post("/format.json", format);

    // Use our router as the middleware, and pass the generated response through `EnablePostCors`
    let mut chain = Chain::new(router);
    chain.link_after(EnablePostCors);

    let addr = ("0.0.0.0", 80);
    println!("listening on {:?}", addr);
    Iron::new(chain).http(addr).unwrap();
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::{color, Backend, Color};
use text_ui::pane::Pane;
//use text_ui::widget::DbgDump;
use text_ui::widget::Widget;
use text_ui::widget::{shared, Line, Linear, Readline, Shared, Text};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::sync::{Arc, RwLock};
use std::thread;

use tokio::prelude::Future;
use weaver::{WeaverClient, WeaverCommand, WeaverNotification, WeaverState};

struct WeaverStateWidget {
    state: Shared<WeaverState>,
    selected: Option<u32>,
}

impl WeaverStateWidget {
    pub fn new(state: Shared<WeaverState>) -> Self {
        let selected = None;
        WeaverStateWidget { state, selected }
    }

    pub fn find_cmd_by_index(&self, i: usize) -> Option<String> {
        let state = self.state.read().unwrap();
        let rv = state
            .command_history
            .iter()
            .rev()
            .skip(i)
            .next()
            .map(|(_, cmd)| cmd.cmd.clone());
        rv
    }
}

fn render_command_summary(
    cmd: &WeaverCommand,
    width: usize,
    maxlines: usize,
    selected: bool,
) -> Pane {
    let mut pane = Pane::new_width(width);
    let (icon, style) = match cmd.status {
        None => ('…', "command.running"),
        Some(0) => ('✔', "command.success"),
        _ => ('X', "command.failed"),
    };
    let status_pane = Pane::new_styled(
        Position::new(0, 0),
        Size::new(1, 1),
        vec![icon.to_string()],
        style,
    );
    pane.push_child(status_pane);
    let subwidth = width - 1;
    let command_line = text_to_lines(cmd.cmd.clone(), subwidth);
    let pos = Position::new(1, 0);
    let textlen = command_line.len();
    let mut offset = textlen;
    let size = Size::new(subwidth as u16, textlen as u16);
    let prefix = match selected {
        true => "selected.",
        false => "",
    };
    pane.push_child(Pane::new_styled(
        pos,
        size,
        command_line,
        &format!("{}command", prefix),
    ));
    if cmd.stdout.len() > 0 {
        let mut stdout = text_to_lines(cmd.stdout.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stdout.len();
        if textlen > maxlines {
            stdout = stdout.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        offset += textlen;
        let size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            size,
            stdout,
            &format!("{}stdout", prefix),
        ));
    }
    if cmd.stderr.len() > 0 {
        let mut stderr = text_to_lines(cmd.stderr.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stderr.len();
        if textlen > maxlines {
            stderr = stderr.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        let size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            size,
            stderr,
            &format!("{}stderr", prefix),
        ));
    }
    pane
}

fn render_command_detail(cmd: &WeaverCommand, size: Size) -> Pane {
    let mut pane = Pane::new_width(size.width as usize);
    let (icon, style) = match cmd.status {
        None => ('…', "command.running"),
        Some(0) => ('✔', "command.success"),
        _ => ('X', "command.failed"),
    };
    let status_pane = Pane::new_styled(
        Position::new(0, 0),
        Size::new(1, 1),
        vec![icon.to_string()],
        style,
    );
    pane.push_child(status_pane);
    let subwidth: usize = size.width as usize - 1;
    let command_line = text_to_lines(cmd.cmd.clone(), subwidth);
    let pos = Position::new(1, 0);
    let textlen = command_line.len();
    let mut offset = textlen;
    let pane_size = Size::new(subwidth as u16, textlen as u16);
    let prefix = "selected.";
    let mut maxlines = size.height as usize - textlen;
    pane.push_child(Pane::new_styled(
        pos,
        pane_size,
        command_line,
        &format!("{}command", prefix),
    ));
    if cmd.stdout.len() > 0 {
        let mut stdout = text_to_lines(cmd.stdout.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stdout.len();
        if textlen > maxlines {
            stdout = stdout.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        maxlines -= textlen;
        offset += textlen;
        let pane_size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            pane_size,
            stdout,
            &format!("{}stdout", prefix),
        ));
    }
    if cmd.stderr.len() > 0 {
        let mut stderr = text_to_lines(cmd.stderr.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stderr.len();
        if textlen > maxlines {
            stderr = stderr.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        let pane_size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            pane_size,
            stderr,
            &format!("{}stderr", prefix),
        ));
    }
    pane
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let height = size.height as usize;
        let mut ctr: usize = 0;
        let state = self.state.read().unwrap();
        let mut children: Vec<Pane> = vec![];
        let mut i = 0;
        let child_width: usize = match self.selected {
            None => size.width as usize,
            Some(_) => size.width as usize / 2,
        };
        for (_i, cmd) in state.command_history.iter().rev() {
            let selected: bool = match self.selected {
                None => false,
                Some(idx) => idx == i,
            };
            let mut child = render_command_summary(cmd, child_width, 10, selected);
            let offset = child.size.height as usize;

            ctr += offset;
            if ctr >= height {
                let spillover = (ctr - height) as u16;
                let clip_pos = Position::new(0, spillover);
                let clip_size = Size::new(child_width as u16, child.size.height - spillover);
                child = child.clip(clip_pos, clip_size).unwrap();
                child.position = Position::new(0, 0);
                ctr = height;
            }
            let child = child.offset(Position::new(0, (height - ctr) as u16));
            children.push(child);
            if selected {
                let child_pos = Position::new(child_width as u16, 0);
                let child_size = Size::new(size.width - (child_width as u16), size.height);
                let child = render_command_detail(cmd, child_size).offset(child_pos);
                children.push(child);
            }
            if ctr == height {
                break;
            }
            i += 1;
        }
        Some(children)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<Readline>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(Readline::new());
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget::new(state.clone()));
        //let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        let mut contentbox = Linear::hbox();
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().finalize();
        if text.len() > 0 {
            self.state
                .write()
                .unwrap()
                .run_command(text.clone())
                .unwrap();
        }
        self.statew.write().unwrap().selected = None;
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            Key::Alt('\r') => self.input.write().unwrap().process_key(Key::Char('\n')),
            Key::Up => {
                let mut statew = self.statew.write().unwrap();
                statew.selected = match statew.selected.take() {
                    None => Some(0),
                    Some(i) => Some(i + 1),
                };
                if let Some(cmd) = statew.find_cmd_by_index(statew.selected.unwrap() as usize) {
                    self.input.write().unwrap().set_line(&cmd);
                };
            }
            Key::Down => {
                let mut statew = self.statew.write().unwrap();
                statew.selected = match statew.selected.take() {
                    None => None,
                    Some(0) => None,
                    Some(i) => {
                        if let Some(cmd) = statew.find_cmd_by_index(i as usize - 1) {
                            self.input.write().unwrap().set_line(&cmd);
                        };
                        Some(i - 1)
                    }
                }
            }
            k => self.input.write().unwrap().process_key(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
    fn style(&self, name: &str) -> (Option<Box<Color>>, Option<Box<Color>>) {
        match name {
            "command" => (None, Some(Box::new(color::Rgb(16, 16, 32)))),
            "stderr" => (None, Some(Box::new(color::Rgb(32, 16, 16)))),
            "stdout" => (None, Some(Box::new(color::Rgb(16, 32, 16)))),
            "input" => (None, Some(Box::new(color::Rgb(32, 32, 32)))),
            "command.running" => (
                Some(Box::new(color::LightYellow)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "command.success" => (
                Some(Box::new(color::LightGreen)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "command.failed" => (Some(Box::new(color::LightRed)), None),
            "selected.command" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(32, 32, 128))),
            ),
            "selected.stderr" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(64, 16, 16))),
            ),
            "selected.stdout" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(16, 64, 16))),
            ),
            "selected.command.running" => (
                Some(Box::new(color::LightYellow)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "selected.command.success" => (
                Some(Box::new(color::LightGreen)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "selected.command.failed" => (Some(Box::new(color::LightRed)), None),
            _ => (None, None),
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<commit_msg>Fix bug in input updates on history iteration<commit_after>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::{color, Backend, Color};
use text_ui::pane::Pane;
//use text_ui::widget::DbgDump;
use text_ui::widget::Widget;
use text_ui::widget::{shared, Line, Linear, Readline, Shared, Text};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::sync::{Arc, RwLock};
use std::thread;

use tokio::prelude::Future;
use weaver::{WeaverClient, WeaverCommand, WeaverNotification, WeaverState};

struct WeaverStateWidget {
    state: Shared<WeaverState>,
    selected: Option<u32>,
}

impl WeaverStateWidget {
    pub fn new(state: Shared<WeaverState>) -> Self {
        let selected = None;
        WeaverStateWidget { state, selected }
    }

    pub fn find_cmd_by_index(&self, i: usize) -> Option<String> {
        let state = self.state.read().unwrap();
        let rv = state
            .command_history
            .iter()
            .rev()
            .skip(i)
            .next()
            .map(|(_, cmd)| cmd.cmd.clone());
        rv
    }
}

fn render_command_summary(
    cmd: &WeaverCommand,
    width: usize,
    maxlines: usize,
    selected: bool,
) -> Pane {
    let mut pane = Pane::new_width(width);
    let (icon, style) = match cmd.status {
        None => ('…', "command.running"),
        Some(0) => ('✔', "command.success"),
        _ => ('X', "command.failed"),
    };
    let status_pane = Pane::new_styled(
        Position::new(0, 0),
        Size::new(1, 1),
        vec![icon.to_string()],
        style,
    );
    pane.push_child(status_pane);
    let subwidth = width - 1;
    let command_line = text_to_lines(cmd.cmd.clone(), subwidth);
    let pos = Position::new(1, 0);
    let textlen = command_line.len();
    let mut offset = textlen;
    let size = Size::new(subwidth as u16, textlen as u16);
    let prefix = match selected {
        true => "selected.",
        false => "",
    };
    pane.push_child(Pane::new_styled(
        pos,
        size,
        command_line,
        &format!("{}command", prefix),
    ));
    if cmd.stdout.len() > 0 {
        let mut stdout = text_to_lines(cmd.stdout.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stdout.len();
        if textlen > maxlines {
            stdout = stdout.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        offset += textlen;
        let size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            size,
            stdout,
            &format!("{}stdout", prefix),
        ));
    }
    if cmd.stderr.len() > 0 {
        let mut stderr = text_to_lines(cmd.stderr.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stderr.len();
        if textlen > maxlines {
            stderr = stderr.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        let size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            size,
            stderr,
            &format!("{}stderr", prefix),
        ));
    }
    pane
}

fn render_command_detail(cmd: &WeaverCommand, size: Size) -> Pane {
    let mut pane = Pane::new_width(size.width as usize);
    let (icon, style) = match cmd.status {
        None => ('…', "command.running"),
        Some(0) => ('✔', "command.success"),
        _ => ('X', "command.failed"),
    };
    let status_pane = Pane::new_styled(
        Position::new(0, 0),
        Size::new(1, 1),
        vec![icon.to_string()],
        style,
    );
    pane.push_child(status_pane);
    let subwidth: usize = size.width as usize - 1;
    let command_line = text_to_lines(cmd.cmd.clone(), subwidth);
    let pos = Position::new(1, 0);
    let textlen = command_line.len();
    let mut offset = textlen;
    let pane_size = Size::new(subwidth as u16, textlen as u16);
    let prefix = "selected.";
    let mut maxlines = size.height as usize - textlen;
    pane.push_child(Pane::new_styled(
        pos,
        pane_size,
        command_line,
        &format!("{}command", prefix),
    ));
    if cmd.stdout.len() > 0 {
        let mut stdout = text_to_lines(cmd.stdout.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stdout.len();
        if textlen > maxlines {
            stdout = stdout.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        maxlines -= textlen;
        offset += textlen;
        let pane_size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            pane_size,
            stdout,
            &format!("{}stdout", prefix),
        ));
    }
    if cmd.stderr.len() > 0 {
        let mut stderr = text_to_lines(cmd.stderr.clone(), subwidth);
        let pos = Position::new(1, offset as u16);
        let mut textlen = stderr.len();
        if textlen > maxlines {
            stderr = stderr.split_off(textlen - maxlines);
            textlen = maxlines;
        }
        let pane_size = Size::new(subwidth as u16, textlen as u16);
        pane.push_child(Pane::new_styled(
            pos,
            pane_size,
            stderr,
            &format!("{}stderr", prefix),
        ));
    }
    pane
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let height = size.height as usize;
        let mut ctr: usize = 0;
        let state = self.state.read().unwrap();
        let mut children: Vec<Pane> = vec![];
        let mut i = 0;
        let child_width: usize = match self.selected {
            None => size.width as usize,
            Some(_) => size.width as usize / 2,
        };
        for (_i, cmd) in state.command_history.iter().rev() {
            let selected: bool = match self.selected {
                None => false,
                Some(idx) => idx == i,
            };
            let mut child = render_command_summary(cmd, child_width, 10, selected);
            let offset = child.size.height as usize;

            ctr += offset;
            if ctr >= height {
                let spillover = (ctr - height) as u16;
                let clip_pos = Position::new(0, spillover);
                let clip_size = Size::new(child_width as u16, child.size.height - spillover);
                child = child.clip(clip_pos, clip_size).unwrap();
                child.position = Position::new(0, 0);
                ctr = height;
            }
            let child = child.offset(Position::new(0, (height - ctr) as u16));
            children.push(child);
            if selected {
                let child_pos = Position::new(child_width as u16, 0);
                let child_size = Size::new(size.width - (child_width as u16), size.height);
                let child = render_command_detail(cmd, child_size).offset(child_pos);
                children.push(child);
            }
            if ctr == height {
                break;
            }
            i += 1;
        }
        Some(children)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<Readline>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(Readline::new());
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget::new(state.clone()));
        //let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        let mut contentbox = Linear::hbox();
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().finalize();
        if text.len() > 0 {
            self.state
                .write()
                .unwrap()
                .run_command(text.clone())
                .unwrap();
        }
        self.statew.write().unwrap().selected = None;
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            Key::Alt('\r') => self.input.write().unwrap().process_key(Key::Char('\n')),
            Key::Up => {
                let mut statew = self.statew.write().unwrap();
                statew.selected = match statew.selected.take() {
                    None => Some(0),
                    Some(i) => Some(i + 1),
                };
                if let Some(cmd) = statew.find_cmd_by_index(statew.selected.unwrap() as usize) {
                    self.input.write().unwrap().set_line(&cmd);
                };
            }
            Key::Down => {
                let mut statew = self.statew.write().unwrap();
                statew.selected = match statew.selected.take() {
                    None => None,
                    Some(0) => None,
                    Some(i) => Some(i - 1),
                };
                match statew.selected {
                    Some(i) => if let Some(cmd) = statew.find_cmd_by_index(i as usize) {
                        self.input.write().unwrap().set_line(&cmd);
                    },
                    None => self.input.write().unwrap().set_line(""),
                }
            }
            k => self.input.write().unwrap().process_key(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
    fn style(&self, name: &str) -> (Option<Box<Color>>, Option<Box<Color>>) {
        match name {
            "command" => (None, Some(Box::new(color::Rgb(16, 16, 32)))),
            "stderr" => (None, Some(Box::new(color::Rgb(32, 16, 16)))),
            "stdout" => (None, Some(Box::new(color::Rgb(16, 32, 16)))),
            "input" => (None, Some(Box::new(color::Rgb(32, 32, 32)))),
            "command.running" => (
                Some(Box::new(color::LightYellow)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "command.success" => (
                Some(Box::new(color::LightGreen)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "command.failed" => (Some(Box::new(color::LightRed)), None),
            "selected.command" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(32, 32, 128))),
            ),
            "selected.stderr" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(64, 16, 16))),
            ),
            "selected.stdout" => (
                Some(Box::new(color::LightWhite)),
                Some(Box::new(color::Rgb(16, 64, 16))),
            ),
            "selected.command.running" => (
                Some(Box::new(color::LightYellow)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "selected.command.success" => (
                Some(Box::new(color::LightGreen)),
                Some(Box::new(color::Rgb(16, 16, 32))),
            ),
            "selected.command.failed" => (Some(Box::new(color::LightRed)), None),
            _ => (None, None),
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::*;

#[derive(Clone, Copy, Default)]
pub struct MutationRoot;

#[async_graphql::Object]
impl MutationRoot {
    /// Process a transaction
    /// Return the transaction if it successfully inserted
    async fn tx(
        &self,
        ctx: &async_graphql::Context<'_>,
        raw_tx: String,
    ) -> async_graphql::Result<TxGva> {
        let tx = TransactionDocumentV10::parse_from_raw_text(&raw_tx)?;

        tx.verify(None)?;

        let data = ctx.data::<SchemaData>()?;

        let server_pubkey = data.server_meta_data.self_pubkey;
        let txs_mempool = data.txs_mempool;

        let tx = data
            .dbs_pool
            .execute(move |dbs| {
                txs_mempool
                    .add_pending_tx(&dbs.bc_db_ro, server_pubkey, &dbs.txs_mp_db, &tx)
                    .map(|()| tx)
            })
            .await??;

        Ok(TxGva::from(&tx))
    }

    /// Process several transactions
    /// Return an array of successfully inserted transactions
    async fn txs(
        &self,
        ctx: &async_graphql::Context<'_>,
        raw_txs: Vec<String>,
    ) -> async_graphql::Result<Vec<TxGva>> {
        let txs = raw_txs
            .iter()
            .map(|raw_tx| TransactionDocumentV10::parse_from_raw_text(&raw_tx))
            .collect::<Result<Vec<TransactionDocumentV10>, _>>()?;

        let data = ctx.data::<SchemaData>()?;

        let server_pubkey = data.server_meta_data.self_pubkey;
        let txs_mempool = data.txs_mempool;

        let mut processed_txs = Vec::with_capacity(txs.len());
        for tx in txs {
            tx.verify(None)?;
            let tx = data
                .dbs_pool
                .execute(move |dbs| {
                    txs_mempool
                        .add_pending_tx(&dbs.bc_db_ro, server_pubkey, &dbs.txs_mp_db, &tx)
                        .map(|()| tx)
                })
                .await??;
            processed_txs.push(TxGva::from(&tx));
        }

        Ok(processed_txs)
    }
}
<commit_msg>[fix] gva: mutation tx: verify field currency<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use crate::*;

#[derive(Clone, Copy, Default)]
pub struct MutationRoot;

#[async_graphql::Object]
impl MutationRoot {
    /// Process a transaction
    /// Return the transaction if it successfully inserted
    async fn tx(
        &self,
        ctx: &async_graphql::Context<'_>,
        raw_tx: String,
    ) -> async_graphql::Result<TxGva> {
        let tx = TransactionDocumentV10::parse_from_raw_text(&raw_tx)?;

        let data = ctx.data::<SchemaData>()?;
        let expected_currency = data.server_meta_data.currency.clone();

        tx.verify(Some(expected_currency))?;

        let server_pubkey = data.server_meta_data.self_pubkey;
        let txs_mempool = data.txs_mempool;

        let tx = data
            .dbs_pool
            .execute(move |dbs| {
                txs_mempool
                    .add_pending_tx(&dbs.bc_db_ro, server_pubkey, &dbs.txs_mp_db, &tx)
                    .map(|()| tx)
            })
            .await??;

        Ok(TxGva::from(&tx))
    }

    /// Process several transactions
    /// Return an array of successfully inserted transactions
    async fn txs(
        &self,
        ctx: &async_graphql::Context<'_>,
        raw_txs: Vec<String>,
    ) -> async_graphql::Result<Vec<TxGva>> {
        let txs = raw_txs
            .iter()
            .map(|raw_tx| TransactionDocumentV10::parse_from_raw_text(&raw_tx))
            .collect::<Result<Vec<TransactionDocumentV10>, _>>()?;

        let data = ctx.data::<SchemaData>()?;
        let expected_currency = data.server_meta_data.currency.clone();

        let server_pubkey = data.server_meta_data.self_pubkey;
        let txs_mempool = data.txs_mempool;

        let mut processed_txs = Vec::with_capacity(txs.len());
        for tx in txs {
            tx.verify(Some(expected_currency.clone()))?;
            let tx = data
                .dbs_pool
                .execute(move |dbs| {
                    txs_mempool
                        .add_pending_tx(&dbs.bc_db_ro, server_pubkey, &dbs.txs_mp_db, &tx)
                        .map(|()| tx)
                })
                .await??;
            processed_txs.push(TxGva::from(&tx));
        }

        Ok(processed_txs)
    }
}
<|endoftext|>


<commit_before>use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Write};

use crate::color;
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
use crate::image::ImageEncoder;

const BITMAPFILEHEADER_SIZE: u32 = 14;
const BITMAPINFOHEADER_SIZE: u32 = 40;
const BITMAPV4HEADER_SIZE: u32 = 108;

/// The representation of a BMP encoder.
pub struct BMPEncoder<'a, W: 'a> {
    writer: &'a mut W,
}

impl<'a, W: Write + 'a> BMPEncoder<'a, W> {
    /// Create a new encoder that writes its output to ```w```.
    pub fn new(w: &'a mut W) -> Self {
        BMPEncoder { writer: w }
    }

    /// Encodes the image ```image```
    /// that has dimensions ```width``` and ```height```
    /// and ```ColorType``` ```c```.
    pub fn encode(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        c: color::ColorType,
    ) -> ImageResult<()> {
        let bmp_header_size = BITMAPFILEHEADER_SIZE;

        let (dib_header_size, written_pixel_size, palette_color_count) = get_pixel_info(c)?;
        let row_pad_size = (4 - (width * written_pixel_size) % 4) % 4; // each row must be padded to a multiple of 4 bytes
        let image_size = width
            .checked_mul(height)
            .and_then(|v| v.checked_mul(written_pixel_size))
            .and_then(|v| v.checked_add(height * row_pad_size))
            .ok_or_else(|| {
                ImageError::Parameter(ParameterError::from_kind(
                    ParameterErrorKind::DimensionMismatch,
                ))
            })?;
        let palette_size = palette_color_count * 4; // all palette colors are BGRA
        let file_size = bmp_header_size + dib_header_size + palette_size + image_size;

        // write BMP header
        self.writer.write_u8(b'B')?;
        self.writer.write_u8(b'M')?;
        self.writer.write_u32::<LittleEndian>(file_size)?; // file size
        self.writer.write_u16::<LittleEndian>(0)?; // reserved 1
        self.writer.write_u16::<LittleEndian>(0)?; // reserved 2
        self.writer
            .write_u32::<LittleEndian>(bmp_header_size + dib_header_size + palette_size)?; // image data offset

        // write DIB header
        self.writer.write_u32::<LittleEndian>(dib_header_size)?;
        self.writer.write_i32::<LittleEndian>(width as i32)?;
        self.writer.write_i32::<LittleEndian>(height as i32)?;
        self.writer.write_u16::<LittleEndian>(1)?; // color planes
        self.writer
            .write_u16::<LittleEndian>((written_pixel_size * 8) as u16)?; // bits per pixel
        if dib_header_size >= BITMAPV4HEADER_SIZE {
            // Assume BGRA32
            self.writer.write_u32::<LittleEndian>(3)?; // compression method - bitfields
        } else {
            self.writer.write_u32::<LittleEndian>(0)?; // compression method - no compression
        }
        self.writer.write_u32::<LittleEndian>(image_size)?;
        self.writer.write_i32::<LittleEndian>(0)?; // horizontal ppm
        self.writer.write_i32::<LittleEndian>(0)?; // vertical ppm
        self.writer.write_u32::<LittleEndian>(palette_color_count)?;
        self.writer.write_u32::<LittleEndian>(0)?; // all colors are important
        if dib_header_size >= BITMAPV4HEADER_SIZE {
            // Assume BGRA32
            self.writer.write_u32::<LittleEndian>(0xff << 16)?; // red mask
            self.writer.write_u32::<LittleEndian>(0xff << 8)?; // green mask
            self.writer.write_u32::<LittleEndian>(0xff << 0)?; // blue mask
            self.writer.write_u32::<LittleEndian>(0xff << 24)?; // alpha mask
            self.writer.write_u32::<LittleEndian>(0x73524742)?; // colorspace - sRGB

            // endpoints (3x3) and gamma (3)
            for _ in 0..12 {
                self.writer.write_u32::<LittleEndian>(0)?;
            }
        }

        // write image data
        match c {
            color::ColorType::Rgb8 => {
                self.encode_rgb(image, width, height, row_pad_size, 3)?
            }
            color::ColorType::Rgba8 => {
                self.encode_rgba(image, width, height, row_pad_size, 4)?
            }
            color::ColorType::L8 => {
                self.encode_gray(image, width, height, row_pad_size, 1)?
            }
            color::ColorType::La8 => {
                self.encode_gray(image, width, height, row_pad_size, 2)?
            }
            _ => {
                return Err(ImageError::IoError(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    &get_unsupported_error_message(c)[..],
                )))
            }
        }

        Ok(())
    }

    fn encode_rgb(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        let x_stride = bytes_per_pixel;
        let y_stride = width * x_stride;
        for row in 0..height {
            // from the bottom up
            let row_start = (height - row - 1) * y_stride;
            for col in 0..width {
                let pixel_start = (row_start + (col * x_stride)) as usize;
                let r = image[pixel_start];
                let g = image[pixel_start + 1];
                let b = image[pixel_start + 2];
                // written as BGR
                self.writer.write_u8(b)?;
                self.writer.write_u8(g)?;
                self.writer.write_u8(r)?;
                // alpha is never written as it's not widely supported
            }

            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn encode_rgba(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        let width = width as usize;
        let height = height as usize;
        let x_stride = bytes_per_pixel as usize;
        let y_stride = width * x_stride;
        for row in (0..height).rev() {
            // from the bottom up
            let row_start = row * y_stride;
            for px in image[row_start..row_start + y_stride].chunks_exact(x_stride) {
                let r = px[0];
                let g = px[1];
                let b = px[2];
                let a = px[3];
                // written as BGRA
                self.writer.write(&[b, g, r, a])?;
            }
            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn encode_gray(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        // write grayscale palette
        for val in 0..256 {
            // each color is written as BGRA, where A is always 0 and since only grayscale is being written, B = G = R = index
            let val = val as u8;
            self.writer.write_u8(val)?;
            self.writer.write_u8(val)?;
            self.writer.write_u8(val)?;
            self.writer.write_u8(0)?;
        }

        // write image data
        let x_stride = bytes_per_pixel;
        let y_stride = width * x_stride;
        for row in 0..height {
            // from the bottom up
            let row_start = (height - row - 1) * y_stride;
            for col in 0..width {
                let pixel_start = (row_start + (col * x_stride)) as usize;
                // color value is equal to the palette index
                self.writer.write_u8(image[pixel_start])?;
                // alpha is never written as it's not widely supported
            }

            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn write_row_pad(&mut self, row_pad_size: u32) -> io::Result<()> {
        for _ in 0..row_pad_size {
            self.writer.write_u8(0)?;
        }

        Ok(())
    }
}

impl<'a, W: Write> ImageEncoder for BMPEncoder<'a, W> {
    fn write_image(
        mut self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: color::ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}

fn get_unsupported_error_message(c: color::ColorType) -> String {
    format!(
        "Unsupported color type {:?}.  Supported types: RGB(8), RGBA(8), Gray(8), GrayA(8).",
        c
    )
}

/// Returns a tuple representing: (dib header size, written pixel size, palette color count).
fn get_pixel_info(c: color::ColorType) -> io::Result<(u32, u32, u32)> {
    let sizes = match c {
        color::ColorType::Rgb8 => (BITMAPINFOHEADER_SIZE, 3, 0),
        color::ColorType::Rgba8 => (BITMAPV4HEADER_SIZE, 4, 0),
        color::ColorType::L8 => (BITMAPINFOHEADER_SIZE, 1, 256),
        color::ColorType::La8 => (BITMAPINFOHEADER_SIZE, 1, 256),
        _ => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                &get_unsupported_error_message(c)[..],
            ))
        }
    };

    Ok(sizes)
}

#[cfg(test)]
mod tests {
    use super::super::BmpDecoder;
    use super::BMPEncoder;
    use crate::color::ColorType;
    use crate::image::ImageDecoder;
    use std::io::Cursor;

    #[cfg(feature = "benchmarks")]
    use test::{Bencher};

    fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
        let mut encoded_data = Vec::new();
        {
            let mut encoder = BMPEncoder::new(&mut encoded_data);
            encoder
                .encode(&image, width, height, c)
                .expect("could not encode image");
        }

        let decoder = BmpDecoder::new(Cursor::new(&encoded_data)).expect("failed to decode");

        let mut buf = vec![0; decoder.total_bytes() as usize];
        decoder.read_image(&mut buf).expect("failed to decode");
        buf
    }

    #[cfg(feature = "benchmarks")]
    #[bench]
    fn bench_encode_rgba(b: &mut Bencher) {
        let mut v= Vec::with_capacity(2000 * 2000);
        let mut x = BMPEncoder::new(&mut v);
        let mut im = vec![0; 2000 * 2000];
        b.iter(|| {
            x.encode_rgba(&mut im, 500, 2000, 0, 4);
        });
    }

    #[test]
    fn round_trip_single_pixel_rgb() {
        let image = [255u8, 0, 0]; // single red pixel
        let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
        assert_eq!(3, decoded.len());
        assert_eq!(255, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
    }

    #[test]
    fn huge_files_return_error() {
        let mut encoded_data = Vec::new();
        let image = vec![0u8; 3 * 40_000 * 40_000]; // 40_000x40_000 pixels, 3 bytes per pixel, allocated on the heap
        let mut encoder = BMPEncoder::new(&mut encoded_data);
        let result = encoder.encode(&image, 40_000, 40_000, ColorType::Rgb8);
        assert!(result.is_err());
    }

    #[test]
    fn round_trip_single_pixel_rgba() {
        let image = [1, 2, 3, 4];
        let decoded = round_trip_image(&image, 1, 1, ColorType::Rgba8);
        assert_eq!(&decoded[..], &image[..]);
    }

    #[test]
    fn round_trip_3px_rgb() {
        let image = [0u8; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
        let _decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
    }

    #[test]
    fn round_trip_gray() {
        let image = [0u8, 1, 2]; // 3 pixels
        let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
        // should be read back as 3 RGB pixels
        assert_eq!(9, decoded.len());
        assert_eq!(0, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
        assert_eq!(1, decoded[3]);
        assert_eq!(1, decoded[4]);
        assert_eq!(1, decoded[5]);
        assert_eq!(2, decoded[6]);
        assert_eq!(2, decoded[7]);
        assert_eq!(2, decoded[8]);
    }

    #[test]
    fn round_trip_graya() {
        let image = [0u8, 0, 1, 0, 2, 0]; // 3 pixels, each with an alpha channel
        let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
        // should be read back as 3 RGB pixels
        assert_eq!(9, decoded.len());
        assert_eq!(0, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
        assert_eq!(1, decoded[3]);
        assert_eq!(1, decoded[4]);
        assert_eq!(1, decoded[5]);
        assert_eq!(2, decoded[6]);
        assert_eq!(2, decoded[7]);
        assert_eq!(2, decoded[8]);
    }
}
<commit_msg>Update encode_rgb as well.<commit_after>use byteorder::{LittleEndian, WriteBytesExt};
use std::io::{self, Write};

use crate::color;
use crate::error::{ImageError, ImageResult, ParameterError, ParameterErrorKind};
use crate::image::ImageEncoder;

const BITMAPFILEHEADER_SIZE: u32 = 14;
const BITMAPINFOHEADER_SIZE: u32 = 40;
const BITMAPV4HEADER_SIZE: u32 = 108;

/// The representation of a BMP encoder.
pub struct BMPEncoder<'a, W: 'a> {
    writer: &'a mut W,
}

impl<'a, W: Write + 'a> BMPEncoder<'a, W> {
    /// Create a new encoder that writes its output to ```w```.
    pub fn new(w: &'a mut W) -> Self {
        BMPEncoder { writer: w }
    }

    /// Encodes the image ```image```
    /// that has dimensions ```width``` and ```height```
    /// and ```ColorType``` ```c```.
    pub fn encode(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        c: color::ColorType,
    ) -> ImageResult<()> {
        let bmp_header_size = BITMAPFILEHEADER_SIZE;

        let (dib_header_size, written_pixel_size, palette_color_count) = get_pixel_info(c)?;
        let row_pad_size = (4 - (width * written_pixel_size) % 4) % 4; // each row must be padded to a multiple of 4 bytes
        let image_size = width
            .checked_mul(height)
            .and_then(|v| v.checked_mul(written_pixel_size))
            .and_then(|v| v.checked_add(height * row_pad_size))
            .ok_or_else(|| {
                ImageError::Parameter(ParameterError::from_kind(
                    ParameterErrorKind::DimensionMismatch,
                ))
            })?;
        let palette_size = palette_color_count * 4; // all palette colors are BGRA
        let file_size = bmp_header_size + dib_header_size + palette_size + image_size;

        // write BMP header
        self.writer.write_u8(b'B')?;
        self.writer.write_u8(b'M')?;
        self.writer.write_u32::<LittleEndian>(file_size)?; // file size
        self.writer.write_u16::<LittleEndian>(0)?; // reserved 1
        self.writer.write_u16::<LittleEndian>(0)?; // reserved 2
        self.writer
            .write_u32::<LittleEndian>(bmp_header_size + dib_header_size + palette_size)?; // image data offset

        // write DIB header
        self.writer.write_u32::<LittleEndian>(dib_header_size)?;
        self.writer.write_i32::<LittleEndian>(width as i32)?;
        self.writer.write_i32::<LittleEndian>(height as i32)?;
        self.writer.write_u16::<LittleEndian>(1)?; // color planes
        self.writer
            .write_u16::<LittleEndian>((written_pixel_size * 8) as u16)?; // bits per pixel
        if dib_header_size >= BITMAPV4HEADER_SIZE {
            // Assume BGRA32
            self.writer.write_u32::<LittleEndian>(3)?; // compression method - bitfields
        } else {
            self.writer.write_u32::<LittleEndian>(0)?; // compression method - no compression
        }
        self.writer.write_u32::<LittleEndian>(image_size)?;
        self.writer.write_i32::<LittleEndian>(0)?; // horizontal ppm
        self.writer.write_i32::<LittleEndian>(0)?; // vertical ppm
        self.writer.write_u32::<LittleEndian>(palette_color_count)?;
        self.writer.write_u32::<LittleEndian>(0)?; // all colors are important
        if dib_header_size >= BITMAPV4HEADER_SIZE {
            // Assume BGRA32
            self.writer.write_u32::<LittleEndian>(0xff << 16)?; // red mask
            self.writer.write_u32::<LittleEndian>(0xff << 8)?; // green mask
            self.writer.write_u32::<LittleEndian>(0xff << 0)?; // blue mask
            self.writer.write_u32::<LittleEndian>(0xff << 24)?; // alpha mask
            self.writer.write_u32::<LittleEndian>(0x73524742)?; // colorspace - sRGB

            // endpoints (3x3) and gamma (3)
            for _ in 0..12 {
                self.writer.write_u32::<LittleEndian>(0)?;
            }
        }

        // write image data
        match c {
            color::ColorType::Rgb8 => {
                self.encode_rgb(image, width, height, row_pad_size, 3)?
            }
            color::ColorType::Rgba8 => {
                self.encode_rgba(image, width, height, row_pad_size, 4)?
            }
            color::ColorType::L8 => {
                self.encode_gray(image, width, height, row_pad_size, 1)?
            }
            color::ColorType::La8 => {
                self.encode_gray(image, width, height, row_pad_size, 2)?
            }
            _ => {
                return Err(ImageError::IoError(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    &get_unsupported_error_message(c)[..],
                )))
            }
        }

        Ok(())
    }

    fn encode_rgb(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        let width = width as usize;
        let height = height as usize;
        let x_stride = bytes_per_pixel as usize;
        let y_stride = width * x_stride;
        for row in (0..height).rev() {
            // from the bottom up
            let row_start = row * y_stride;
            for px in image[row_start.. row_start + y_stride].chunks_exact(x_stride) {
                let r = px[0];
                let g = px[1];
                let b = px[2];
                // written as BGR
                self.writer.write(&[b, g, r])?;
            }
            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn encode_rgba(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        let width = width as usize;
        let height = height as usize;
        let x_stride = bytes_per_pixel as usize;
        let y_stride = width * x_stride;
        for row in (0..height).rev() {
            // from the bottom up
            let row_start = row * y_stride;
            for px in image[row_start..row_start + y_stride].chunks_exact(x_stride) {
                let r = px[0];
                let g = px[1];
                let b = px[2];
                let a = px[3];
                // written as BGRA
                self.writer.write(&[b, g, r, a])?;
            }
            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn encode_gray(
        &mut self,
        image: &[u8],
        width: u32,
        height: u32,
        row_pad_size: u32,
        bytes_per_pixel: u32,
    ) -> io::Result<()> {
        // write grayscale palette
        for val in 0..256 {
            // each color is written as BGRA, where A is always 0 and since only grayscale is being written, B = G = R = index
            let val = val as u8;
            self.writer.write_u8(val)?;
            self.writer.write_u8(val)?;
            self.writer.write_u8(val)?;
            self.writer.write_u8(0)?;
        }

        // write image data
        let x_stride = bytes_per_pixel;
        let y_stride = width * x_stride;
        for row in 0..height {
            // from the bottom up
            let row_start = (height - row - 1) * y_stride;
            for col in 0..width {
                let pixel_start = (row_start + (col * x_stride)) as usize;
                // color value is equal to the palette index
                self.writer.write_u8(image[pixel_start])?;
                // alpha is never written as it's not widely supported
            }

            self.write_row_pad(row_pad_size)?;
        }

        Ok(())
    }

    fn write_row_pad(&mut self, row_pad_size: u32) -> io::Result<()> {
        for _ in 0..row_pad_size {
            self.writer.write_u8(0)?;
        }

        Ok(())
    }
}

impl<'a, W: Write> ImageEncoder for BMPEncoder<'a, W> {
    fn write_image(
        mut self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: color::ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}

fn get_unsupported_error_message(c: color::ColorType) -> String {
    format!(
        "Unsupported color type {:?}.  Supported types: RGB(8), RGBA(8), Gray(8), GrayA(8).",
        c
    )
}

/// Returns a tuple representing: (dib header size, written pixel size, palette color count).
fn get_pixel_info(c: color::ColorType) -> io::Result<(u32, u32, u32)> {
    let sizes = match c {
        color::ColorType::Rgb8 => (BITMAPINFOHEADER_SIZE, 3, 0),
        color::ColorType::Rgba8 => (BITMAPV4HEADER_SIZE, 4, 0),
        color::ColorType::L8 => (BITMAPINFOHEADER_SIZE, 1, 256),
        color::ColorType::La8 => (BITMAPINFOHEADER_SIZE, 1, 256),
        _ => {
            return Err(io::Error::new(
                io::ErrorKind::InvalidInput,
                &get_unsupported_error_message(c)[..],
            ))
        }
    };

    Ok(sizes)
}

#[cfg(test)]
mod tests {
    use super::super::BmpDecoder;
    use super::BMPEncoder;
    use crate::color::ColorType;
    use crate::image::ImageDecoder;
    use std::io::Cursor;

    #[cfg(feature = "benchmarks")]
    use test::{Bencher};

    fn round_trip_image(image: &[u8], width: u32, height: u32, c: ColorType) -> Vec<u8> {
        let mut encoded_data = Vec::new();
        {
            let mut encoder = BMPEncoder::new(&mut encoded_data);
            encoder
                .encode(&image, width, height, c)
                .expect("could not encode image");
        }

        let decoder = BmpDecoder::new(Cursor::new(&encoded_data)).expect("failed to decode");

        let mut buf = vec![0; decoder.total_bytes() as usize];
        decoder.read_image(&mut buf).expect("failed to decode");
        buf
    }

    #[cfg(feature = "benchmarks")]
    #[bench]
    fn bench_encode_rgb(b: &mut Bencher) {
        let mut v= Vec::with_capacity(2000 * 2000);
        let mut x = BMPEncoder::new(&mut v);
        let mut im = vec![0; 2000 * 2000];
        b.iter(|| {
            x.encode_rgb(&mut im, 500, 2000, 0, 4);
        });
    }

    #[cfg(feature = "benchmarks")]
    #[bench]
    fn bench_encode_rgba(b: &mut Bencher) {
        let mut v= Vec::with_capacity(2000 * 2000);
        let mut x = BMPEncoder::new(&mut v);
        let mut im = vec![0; 2000 * 2000];
        b.iter(|| {
            x.encode_rgba(&mut im, 500, 2000, 0, 4);
        });
    }

    #[test]
    fn round_trip_single_pixel_rgb() {
        let image = [255u8, 0, 0]; // single red pixel
        let decoded = round_trip_image(&image, 1, 1, ColorType::Rgb8);
        assert_eq!(3, decoded.len());
        assert_eq!(255, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
    }

    #[test]
    fn huge_files_return_error() {
        let mut encoded_data = Vec::new();
        let image = vec![0u8; 3 * 40_000 * 40_000]; // 40_000x40_000 pixels, 3 bytes per pixel, allocated on the heap
        let mut encoder = BMPEncoder::new(&mut encoded_data);
        let result = encoder.encode(&image, 40_000, 40_000, ColorType::Rgb8);
        assert!(result.is_err());
    }

    #[test]
    fn round_trip_single_pixel_rgba() {
        let image = [1, 2, 3, 4];
        let decoded = round_trip_image(&image, 1, 1, ColorType::Rgba8);
        assert_eq!(&decoded[..], &image[..]);
    }

    #[test]
    fn round_trip_3px_rgb() {
        let image = [0u8; 3 * 3 * 3]; // 3x3 pixels, 3 bytes per pixel
        let _decoded = round_trip_image(&image, 3, 3, ColorType::Rgb8);
    }

    #[test]
    fn round_trip_gray() {
        let image = [0u8, 1, 2]; // 3 pixels
        let decoded = round_trip_image(&image, 3, 1, ColorType::L8);
        // should be read back as 3 RGB pixels
        assert_eq!(9, decoded.len());
        assert_eq!(0, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
        assert_eq!(1, decoded[3]);
        assert_eq!(1, decoded[4]);
        assert_eq!(1, decoded[5]);
        assert_eq!(2, decoded[6]);
        assert_eq!(2, decoded[7]);
        assert_eq!(2, decoded[8]);
    }

    #[test]
    fn round_trip_graya() {
        let image = [0u8, 0, 1, 0, 2, 0]; // 3 pixels, each with an alpha channel
        let decoded = round_trip_image(&image, 1, 3, ColorType::La8);
        // should be read back as 3 RGB pixels
        assert_eq!(9, decoded.len());
        assert_eq!(0, decoded[0]);
        assert_eq!(0, decoded[1]);
        assert_eq!(0, decoded[2]);
        assert_eq!(1, decoded[3]);
        assert_eq!(1, decoded[4]);
        assert_eq!(1, decoded[5]);
        assert_eq!(2, decoded[6]);
        assert_eq!(2, decoded[7]);
        assert_eq!(2, decoded[8]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.

//! Bstream module.
//! This is a blocking stream designed to block on read/write until

use std::result::Result;
use std::net::TcpStream;
use std::io::{Read, Write, Error};

use super::readbuffer::ReadBuffer;

/// Represents the result of attempting a read on the underlying file descriptor
pub type ReadResult = Result<Vec<u8>, Error>;

/// Represents the result attempting a write on the underlying fild descriptor
pub type WriteResult = Result<(), Error>;

/// States the current stream can be in
#[derive(PartialEq, Clone)]
enum ReadState {
    /// Currently reading the payload length
    PayloadLen,
    /// Currently reading the payload
    Payload
}

pub struct Bstream {
    /// Current state
    state: ReadState,
    /// Underlying std::net::TcpStream
    stream: TcpStream,
    /// Message buffer
    buffer: ReadBuffer
}

impl Bstream {

    /// Returns a new Bstream
    pub fn new(stream: TcpStream) -> Bstream {
        Bstream {
            state: ReadState::PayloadLen,
            stream: stream,
            buffer: ReadBuffer::new()
        }
    }

    /// Performs a blocking read and returns when a complete message
    /// has been returned, or an error has occured
    pub fn read(&mut self) -> ReadResult {
        loop {
            // Create a buffer for this specific read iteration
            let count = self.buffer.remaining();
            let mut buffer = Vec::<u8>::with_capacity(count as usize);
            unsafe { buffer.set_len(count as usize); }

            let result = self.stream.read(&mut buffer[..]);
            if result.is_err() {
                return Err(result.unwrap_err());
            }

            let num_read = result.unwrap();
            for x in 0..num_read {
                self.buffer.push(buffer[x]);
            }

            if self.buffer.remaining() == 0 {
                if self.state == ReadState::PayloadLen {
                    self.buffer.calc_payload_len();
                    let p_len = self.buffer.payload_len();
                    self.buffer.set_capacity(p_len);
                    self.state = ReadState::Payload;
                } else { // Payload completely read
                    self.buffer.reset();
                    self.state = ReadState::PayloadLen;
                    break;
                }
            }
        }

        let mut buffer = self.buffer.drain_queue();
        // This should always be .len() of 1
        // if it isn't - we're doing some bad stuff in here
        if buffer.len() != 1 {
            panic!("Error - Bstream.read() - Internal buffer was not equal to one...?")
        }

        match buffer.pop() {
            Some(buf) => Ok(buf),
            None => unimplemented!()
        }
    }

    /// Performs a blocking write operation and returns the complete buffer has
    /// been written, or an error has occured
    pub fn write(&mut self, buffer: &Vec<u8>) -> WriteResult {
        let mut plen_buf = [0u8; 2];
        plen_buf[0] = (buffer.len() as u16 & 0b1111_1111u16 << 8) as u8;
        plen_buf[1] = (buffer.len() as u16 & 0b1111_1111u16) as u8;

        let mut n_buffer = Vec::<u8>::with_capacity(buffer.len() + 2);
        n_buffer.push(plen_buf[0]);
        n_buffer.push(plen_buf[1]);

        for x in 0..buffer.len() {
            n_buffer.push(buffer[x]);
        }

        match self.stream.write_all(&n_buffer[..]) {
            Ok(()) => {
                let _ = self.stream.flush();
                Ok(())
            }
            Err(e) => Err(e)
        }
    }
}

impl Clone for Bstream {
    fn clone(&self) -> Bstream {
        Bstream {
            state: self.state.clone(),
            stream: self.stream.try_clone().unwrap(),
            buffer: self.buffer.clone()
        }
    }
}
<commit_msg>Updated debugging statements<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// 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/.

//! Bstream module.
//! This is a blocking stream designed to block on read/write until

use std::result::Result;
use std::net::TcpStream;
use std::io::{Read, Write, Error};

use super::readbuffer::ReadBuffer;

/// Represents the result of attempting a read on the underlying file descriptor
pub type ReadResult = Result<Vec<u8>, Error>;

/// Represents the result attempting a write on the underlying fild descriptor
pub type WriteResult = Result<(), Error>;

/// States the current stream can be in
#[derive(PartialEq, Clone)]
enum ReadState {
    /// Currently reading the payload length
    PayloadLen,
    /// Currently reading the payload
    Payload
}

pub struct Bstream {
    /// Current state
    state: ReadState,
    /// Underlying std::net::TcpStream
    stream: TcpStream,
    /// Message buffer
    buffer: ReadBuffer
}

impl Bstream {

    /// Returns a new Bstream
    pub fn new(stream: TcpStream) -> Bstream {
        Bstream {
            state: ReadState::PayloadLen,
            stream: stream,
            buffer: ReadBuffer::new()
        }
    }

    /// Performs a blocking read and returns when a complete message
    /// has been returned, or an error has occured
    pub fn read(&mut self) -> ReadResult {

        println!("simple stream read loop =============");

        loop {
            // Create a buffer for this specific read iteration
            let count = self.buffer.remaining();
            let mut buffer = Vec::<u8>::with_capacity(count as usize);
            unsafe { buffer.set_len(count as usize); }

            let result = self.stream.read(&mut buffer[..]);
            if result.is_err() {
                return Err(result.unwrap_err());
            }

            let num_read = result.unwrap();
            for x in 0..num_read {
                self.buffer.push(buffer[x]);
            }

            if self.buffer.remaining() == 0 {
                if self.state == ReadState::PayloadLen {

                    println!("Payload length read complete");

                    self.buffer.calc_payload_len();
                    let p_len = self.buffer.payload_len();

                    println!("Payload length: {}", p_len);

                    self.buffer.set_capacity(p_len);
                    self.state = ReadState::Payload;
                } else { // Payload completely read

                    println!("Payload read complete");

                    self.buffer.reset();
                    self.state = ReadState::PayloadLen;
                    break;
                }
            }
        }

        println!("simple stream read loop finished =============");

        let mut buffer = self.buffer.drain_queue();

        println!("Queue drained");
        println!("buffer.len: {}", buffer.len());

        // This should always be .len() of 1
        // if it isn't - we're doing some bad stuff in here
        if buffer.len() != 1 {
            panic!("Error - Bstream.read() - Internal buffer was not equal to one...?")
        }

        match buffer.pop() {
            Some(buf) => Ok(buf),
            None => unimplemented!()
        }
    }

    /// Performs a blocking write operation and returns the complete buffer has
    /// been written, or an error has occured
    pub fn write(&mut self, buffer: &Vec<u8>) -> WriteResult {
        let mut plen_buf = [0u8; 2];
        plen_buf[0] = (buffer.len() as u16 & 0b1111_1111u16 << 8) as u8;
        plen_buf[1] = (buffer.len() as u16 & 0b1111_1111u16) as u8;

        let mut n_buffer = Vec::<u8>::with_capacity(buffer.len() + 2);
        n_buffer.push(plen_buf[0]);
        n_buffer.push(plen_buf[1]);

        for x in 0..buffer.len() {
            n_buffer.push(buffer[x]);
        }

        match self.stream.write_all(&n_buffer[..]) {
            Ok(()) => {
                let _ = self.stream.flush();
                Ok(())
            }
            Err(e) => Err(e)
        }
    }
}

impl Clone for Bstream {
    fn clone(&self) -> Bstream {
        Bstream {
            state: self.state.clone(),
            stream: self.stream.try_clone().unwrap(),
            buffer: self.buffer.clone()
        }
    }
}
<|endoftext|>