<commit_before>extern crate iron;
extern crate url;
extern crate bodyparser;
extern crate rustc_serialize;
extern crate rand;
extern crate rusqlite;
extern crate time;

use iron::prelude::*;
use iron::{ Url, status };
use iron::modifiers::Redirect;
use url::Host;
use rand::os::OsRng;
use rand::Rng;
use time::get_time;
use std::io::{Error, ErrorKind};

use models::{ Register, Reconfigure, ConnectionKey, IpMapping };

fn iron_err_from_string(err: &str) -> IronResult<Response> {
    Err(IronError::new(Error::new(ErrorKind::Other, err), status::InternalServerError))
}

/// # Generate new request tokens
/// other API endpoints require a valid token stored in the database, hitting this endpoint generates and returns a new token (as well as stores it in the database for later validation)
pub fn token_handler(req: &mut Request) -> IronResult<Response> {
    //Create a RNG, early exit if fail
    let mut rng = match OsRng::new() {
        Ok(rng) => rng,
        Err(err) => return Err(IronError::new(err, status::InternalServerError))
    };

    //Get the SQL connection, early exit if fail
    let conn = match req.extensions.get::<ConnectionKey>() {
        Some(conn) => conn,
        None => return iron_err_from_string("No SQL Connection")
    };

    //Generate a completely random token
    //We sacrifice 1 bit of randomness to ensure tokens are always positive
    let token = rng.gen::<i64>().abs();

    //Insert token into DB
    match conn.execute("INSERT INTO tokens (id, time_created) VALUES ($1, $2)", &[&token, &get_time().sec]) {

        //If we inserted zero values something is wrong!
        Ok(0) => iron_err_from_string("Failed to insert token into DB"),

        //Inserted some number of values, cool
        Ok(_) => Ok(Response::with((status::Ok, token.to_string()))),

        //Error!
        Err(err) => Err(IronError::new(err, status::InternalServerError))
    }
}

pub fn redirect_handler(req: &mut Request) -> IronResult<Response> {

    //Get the SQL connection, early exit if fail
    let conn = match req.extensions.get::<ConnectionKey>() {
        Some(conn) => conn,
        None => return iron_err_from_string("No SQL Connection")
    };

    //Prepare a query to lookup IP
    let mut stmt = match conn.prepare("SELECT internal_ip, internal_port FROM redirects WHERE public_ip = ?") {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(stmt) => stmt
    };

    //Convert IP address to a integer
    let ip = req.remote_addr.to_string();

    //Lookup the correct URL to redirect to based on the origin IP
    let optionalAddr = match stmt.query_map(&[&ip], |row| { IpMapping { ip: row.get::<String>(0), port: row.get::<i32>(1) } } ) {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(mut result) => result.nth(0)
    };

    //The nth item may not exist, so we need to unwrap that
    let addrResult = match optionalAddr {
        None => return Err(IronError::new(Error::new(ErrorKind::Other, "No mapping found for this address"), status::NotFound)),
        Some(addr) => addr
    };

    //The nth item may have been an error, so we need to unwrap that
    let addr = match addrResult {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(result) => result
    };

    // Create a mutable clone of the request url
    // Change url host to a looked up address while maintaining the rest of the request
    let mut incoming_url = req.url.clone();
    incoming_url.host = Host::Domain(addr.ip);
    incoming_url.port = addr.port as u16;

    Ok(Response::with((status::TemporaryRedirect, Redirect(incoming_url))))
}

pub fn register_handler(req: &mut Request) -> IronResult<Response> {
    let body = req.get::<bodyparser::Struct<Register>>();
    match body {
        Ok(Some(body)) => {
            if !check_invite(&body.invite) {
                return Ok(Response::with((status::Forbidden, "Invalid Invite Code")))
            }

            if !check_key(&body.key) {
                return Ok(Response::with((status::Forbidden, "Invalid Key")))
            }

            if !check_url(&body.url) {
                return Ok(Response::with((status::BadRequest, "Invalid Url")))
            }

            println!("Parsed Body:\n{:?}", body);
            match save(body.key, body.url) {
                Ok(_) => Ok(Response::with((status::Ok, "Ok"))),
                Err(err) => Ok(Response::with((status::InternalServerError  , err)))
            }
        },
        Ok(None) => {
            println!("Empty Body");
            Ok(Response::with((status::NoContent, "No Content Provided")))
        },
        Err(err) => {
            println!("Error: {:?}", err);
            Ok(Response::with((status::BadRequest, "Failed to parse body")))
        }
    }
}

pub fn reconfigure_handler(req: &mut Request) -> IronResult<Response> {
    let body = req.get::<bodyparser::Struct<Reconfigure>>();
    panic!()
}

fn check_invite(code: &String) -> bool {
    return code == "Invite";
}

fn check_key(key: &String) -> bool {
    return key == "1234567890";
}

fn check_url(url: &String) -> bool {
    Url::parse(&url).is_ok()
}

fn save(key: String, url: String) -> Result<(), &'static str>{
    return Err("Database not implemented")
}
<commit_msg>Fixed converting IP address to string (getting rid of the source port, we don't care)<commit_after>extern crate iron;
extern crate url;
extern crate bodyparser;
extern crate rustc_serialize;
extern crate rand;
extern crate rusqlite;
extern crate time;

use iron::prelude::*;
use iron::{ Url, status };
use iron::modifiers::Redirect;
use url::Host;
use rand::os::OsRng;
use rand::Rng;
use time::get_time;
use std::io::{Error, ErrorKind};
use std::net::SocketAddr::{ V4, V6 };

use models::{ Register, Reconfigure, ConnectionKey, IpMapping };

fn iron_err_from_string(err: &str) -> IronResult<Response> {
    Err(IronError::new(Error::new(ErrorKind::Other, err), status::InternalServerError))
}

/// # Generate new request tokens
/// other API endpoints require a valid token stored in the database, hitting this endpoint generates and returns a new token (as well as stores it in the database for later validation)
pub fn token_handler(req: &mut Request) -> IronResult<Response> {
    //Create a RNG, early exit if fail
    let mut rng = match OsRng::new() {
        Ok(rng) => rng,
        Err(err) => return Err(IronError::new(err, status::InternalServerError))
    };

    //Get the SQL connection, early exit if fail
    let conn = match req.extensions.get::<ConnectionKey>() {
        Some(conn) => conn,
        None => return iron_err_from_string("No SQL Connection")
    };

    //Generate a completely random token
    //We sacrifice 1 bit of randomness to ensure tokens are always positive
    let token = rng.gen::<i64>().abs();

    //Insert token into DB
    match conn.execute("INSERT INTO tokens (id, time_created) VALUES ($1, $2)", &[&token, &get_time().sec]) {

        //If we inserted zero values something is wrong!
        Ok(0) => iron_err_from_string("Failed to insert token into DB"),

        //Inserted some number of values, cool
        Ok(_) => Ok(Response::with((status::Ok, token.to_string()))),

        //Error!
        Err(err) => Err(IronError::new(err, status::InternalServerError))
    }
}

pub fn redirect_handler(req: &mut Request) -> IronResult<Response> {

    //Get the SQL connection, early exit if fail
    let conn = match req.extensions.get::<ConnectionKey>() {
        Some(conn) => conn,
        None => return iron_err_from_string("No SQL Connection")
    };

    //Prepare a query to lookup IP
    let mut stmt = match conn.prepare("SELECT internal_ip, internal_port FROM redirects WHERE public_ip = ?") {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(stmt) => stmt
    };

    //Convert IP address to a string
    let ip = match req.remote_addr {
        V4(addr) => addr.ip().to_string(),
        V6(addr) => addr.ip().to_string()
    };

    //Lookup the correct URL to redirect to based on the origin IP
    let optionalAddr = match stmt.query_map(&[&ip], |row| { IpMapping { ip: row.get::<String>(0), port: row.get::<i32>(1) } } ) {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(mut result) => result.nth(0)
    };

    //The nth item may not exist, so we need to unwrap that
    let addrResult = match optionalAddr {
        None => return Err(IronError::new(Error::new(ErrorKind::Other, "No mapping found for this address"), status::NotFound)),
        Some(addr) => addr
    };

    //The nth item may have been an error, so we need to unwrap that
    let addr = match addrResult {
        Err(err) => return Err(IronError::new(err, status::InternalServerError)),
        Ok(result) => result
    };

    // Create a mutable clone of the request url
    // Change url host to a looked up address while maintaining the rest of the request
    let mut incoming_url = req.url.clone();
    incoming_url.host = Host::Domain(addr.ip);
    incoming_url.port = addr.port as u16;

    Ok(Response::with((status::TemporaryRedirect, Redirect(incoming_url))))
}

pub fn register_handler(req: &mut Request) -> IronResult<Response> {
    let body = req.get::<bodyparser::Struct<Register>>();
    match body {
        Ok(Some(body)) => {
            if !check_invite(&body.invite) {
                return Ok(Response::with((status::Forbidden, "Invalid Invite Code")))
            }

            if !check_key(&body.key) {
                return Ok(Response::with((status::Forbidden, "Invalid Key")))
            }

            if !check_url(&body.url) {
                return Ok(Response::with((status::BadRequest, "Invalid Url")))
            }

            println!("Parsed Body:\n{:?}", body);
            match save(body.key, body.url) {
                Ok(_) => Ok(Response::with((status::Ok, "Ok"))),
                Err(err) => Ok(Response::with((status::InternalServerError  , err)))
            }
        },
        Ok(None) => {
            println!("Empty Body");
            Ok(Response::with((status::NoContent, "No Content Provided")))
        },
        Err(err) => {
            println!("Error: {:?}", err);
            Ok(Response::with((status::BadRequest, "Failed to parse body")))
        }
    }
}

pub fn reconfigure_handler(req: &mut Request) -> IronResult<Response> {
    let body = req.get::<bodyparser::Struct<Reconfigure>>();
    panic!()
}

fn check_invite(code: &String) -> bool {
    return code == "Invite";
}

fn check_key(key: &String) -> bool {
    return key == "1234567890";
}

fn check_url(url: &String) -> bool {
    Url::parse(&url).is_ok()
}

fn save(key: String, url: String) -> Result<(), &'static str>{
    return Err("Database not implemented")
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use env::Env;
use analysis::bounds::{Bounds, BoundType};
use analysis::ffi_type::ffi_type;
use analysis::parameter::Parameter;
use analysis::ref_mode::RefMode;
use analysis::rust_type::parameter_rust_type;
use analysis::trampolines::Trampoline;
use nameutil;
use super::general::version_condition;
use super::return_value::ToReturnValue;
use super::trampoline_from_glib::TrampolineFromGlib;
use super::trampoline_to_glib::TrampolineToGlib;
use traits::IntoString;

pub fn generate(w: &mut Write, env: &Env, analysis: &Trampoline,
                in_trait: bool, object_name: &str) -> Result<()> {
    try!(writeln!(w, ""));
    let (bounds, end) = if in_trait {
        ("<T>", "")
    } else {
        ("", " {")
    };

    let params_str = trampoline_parameters(env, analysis);
    let func_str = func_string(env, analysis, None::<(&str, &str)>);
    let ret_str = trampoline_returns(env, analysis);

    try!(version_condition(w, env, analysis.version, false, 0));
    try!(writeln!(w, "unsafe extern \"C\" fn {}{}({}, f: glib_ffi::gpointer){}{}",
                  analysis.name, bounds, params_str, ret_str, end));
    if in_trait {
        try!(writeln!(w, "where T: IsA<{}> {{", object_name));
    }
    try!(writeln!(w, "\tcallback_guard!();"));
    try!(writeln!(w, "\tlet f: &Box_<{}> = transmute(f);", func_str));
    let call = trampoline_call_func(env, analysis, in_trait);
    try!(writeln!(w, "\t{}", call));
    try!(writeln!(w, "}}"));

    Ok(())
}

pub fn func_string(env: &Env, analysis: &Trampoline,
                   bound_replace: Option<(&str, &str)>) -> String {
    let param_str = func_parameters(env, analysis, bound_replace);
    let return_str = func_returns(env, analysis);

    format!("Fn({}){} + 'static", param_str, return_str)
}

fn func_parameters(env: &Env, analysis: &Trampoline,
                   bound_replace: Option<(&str, &str)>) -> String {
    let mut param_str = String::with_capacity(100);

    for (pos, par) in analysis.parameters.iter().enumerate() {
        if pos > 0 { param_str.push_str(", ") }
        let s = func_parameter(env, par, &analysis.bounds, bound_replace);
        param_str.push_str(&s);
    }

    param_str
}

fn func_parameter(env: &Env, par: &Parameter, bounds: &Bounds,
                  bound_replace: Option<(&str, &str)>) -> String {
    //TODO: restore mutable support
    //let mut_str = if par.ref_mode == RefMode::ByRefMut { "mut " } else { "" };
    let mut_str = "";
    let ref_mode = if par.ref_mode == RefMode::ByRefMut {
        RefMode::ByRef
    } else {
        par.ref_mode
    };

    let type_str: String;
    match bounds.get_parameter_alias_info(&par.name) {
        Some((t, bound_type)) => {
            match bound_type {
                BoundType::IsA => if *par.nullable {
                    type_str = format!("Option<&{}{}>", mut_str, t)
                } else {
                    let t = if let Some((from, to)) = bound_replace {
                        if from == t { to } else { t }
                    } else {
                        t
                    };
                    type_str = format!("&{}{}", mut_str, t)
                },
                BoundType::AsRef  => type_str = t.to_owned(),
            }
        }
        None => {
            let rust_type = parameter_rust_type(env, par.typ, par.direction,
                                                par.nullable, ref_mode);
            type_str = rust_type.into_string();
        }
    }
    type_str
}

fn func_returns(env: &Env, analysis: &Trampoline) -> String {
    if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        analysis.ret.to_return_value(&env)
    }
}

fn trampoline_parameters(env: &Env, analysis: &Trampoline) -> String {
    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &analysis.parameters {
        let par_str = trampoline_parameter(env, par);
        parameter_strs.push(par_str);
    }

    parameter_strs.join(", ")
}

fn trampoline_parameter(env: &Env, par: &Parameter) -> String {
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    format!("{}: {}", nameutil::mangle_keywords(&*par.name), ffi_type.into_string())
}

fn trampoline_returns(env: &Env, analysis: &Trampoline) -> String {
    if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        let ffi_type = ffi_type(env, analysis.ret.typ, &analysis.ret.c_type);
        format!(" -> {}", ffi_type.into_string())
    }
}

fn trampoline_call_func(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
    let params = trampoline_call_parameters(env, analysis, in_trait);
    let ret = if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        analysis.ret.trampoline_to_glib(&env.library)
    };
    format!("f({}){}", params, ret)
}

fn trampoline_call_parameters(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
    let mut need_downcast = in_trait;
    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &analysis.parameters {
        let par_str = par.trampoline_from_glib(env, need_downcast);
        parameter_strs.push(par_str);
        need_downcast = false;  //Only downcast first parameter
    }

    parameter_strs.join(", ")
}
<commit_msg>Fix signal parameters with Options<commit_after>use std::io::{Result, Write};

use env::Env;
use analysis::bounds::{Bounds, BoundType};
use analysis::ffi_type::ffi_type;
use analysis::parameter::Parameter;
use analysis::ref_mode::RefMode;
use analysis::rust_type::parameter_rust_type;
use analysis::trampolines::Trampoline;
use nameutil;
use super::general::version_condition;
use super::return_value::ToReturnValue;
use super::trampoline_from_glib::TrampolineFromGlib;
use super::trampoline_to_glib::TrampolineToGlib;
use traits::IntoString;

pub fn generate(w: &mut Write, env: &Env, analysis: &Trampoline,
                in_trait: bool, object_name: &str) -> Result<()> {
    try!(writeln!(w, ""));
    let (bounds, end) = if in_trait {
        ("<T>", "")
    } else {
        ("", " {")
    };

    let params_str = trampoline_parameters(env, analysis);
    let func_str = func_string(env, analysis, None::<(&str, &str)>);
    let ret_str = trampoline_returns(env, analysis);

    try!(version_condition(w, env, analysis.version, false, 0));
    try!(writeln!(w, "unsafe extern \"C\" fn {}{}({}, f: glib_ffi::gpointer){}{}",
                  analysis.name, bounds, params_str, ret_str, end));
    if in_trait {
        try!(writeln!(w, "where T: IsA<{}> {{", object_name));
    }
    try!(writeln!(w, "\tcallback_guard!();"));
    try!(writeln!(w, "\tlet f: &Box_<{}> = transmute(f);", func_str));
    let call = trampoline_call_func(env, analysis, in_trait);
    try!(writeln!(w, "\t{}", call));
    try!(writeln!(w, "}}"));

    Ok(())
}

pub fn func_string(env: &Env, analysis: &Trampoline,
                   bound_replace: Option<(&str, &str)>) -> String {
    let param_str = func_parameters(env, analysis, bound_replace);
    let return_str = func_returns(env, analysis);

    format!("Fn({}){} + 'static", param_str, return_str)
}

fn func_parameters(env: &Env, analysis: &Trampoline,
                   bound_replace: Option<(&str, &str)>) -> String {
    let mut param_str = String::with_capacity(100);

    for (pos, par) in analysis.parameters.iter().enumerate() {
        if pos > 0 { param_str.push_str(", ") }
        let s = func_parameter(env, par, &analysis.bounds, bound_replace);
        param_str.push_str(&s);
    }

    param_str
}

fn func_parameter(env: &Env, par: &Parameter, bounds: &Bounds,
                  bound_replace: Option<(&str, &str)>) -> String {
    //TODO: restore mutable support
    //let mut_str = if par.ref_mode == RefMode::ByRefMut { "mut " } else { "" };
    let mut_str = "";
    let ref_mode = if par.ref_mode == RefMode::ByRefMut {
        RefMode::ByRef
    } else {
        par.ref_mode
    };

    let type_str: String;
    match bounds.get_parameter_alias_info(&par.name) {
        Some((t, bound_type)) => {
            match bound_type {
                BoundType::IsA => if *par.nullable {
                    type_str = format!("&Option<{}{}>", mut_str, t)
                } else {
                    let t = if let Some((from, to)) = bound_replace {
                        if from == t { to } else { t }
                    } else {
                        t
                    };
                    type_str = format!("&{}{}", mut_str, t)
                },
                BoundType::AsRef  => type_str = t.to_owned(),
            }
        }
        None => {
            let rust_type = parameter_rust_type(env, par.typ, par.direction,
                                                par.nullable, ref_mode);
            type_str = rust_type.into_string().replace("Option<&", "&Option<");
        }
    }
    type_str
}

fn func_returns(env: &Env, analysis: &Trampoline) -> String {
    if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        analysis.ret.to_return_value(&env)
    }
}

fn trampoline_parameters(env: &Env, analysis: &Trampoline) -> String {
    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &analysis.parameters {
        let par_str = trampoline_parameter(env, par);
        parameter_strs.push(par_str);
    }

    parameter_strs.join(", ")
}

fn trampoline_parameter(env: &Env, par: &Parameter) -> String {
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    format!("{}: {}", nameutil::mangle_keywords(&*par.name), ffi_type.into_string())
}

fn trampoline_returns(env: &Env, analysis: &Trampoline) -> String {
    if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        let ffi_type = ffi_type(env, analysis.ret.typ, &analysis.ret.c_type);
        format!(" -> {}", ffi_type.into_string())
    }
}

fn trampoline_call_func(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
    let params = trampoline_call_parameters(env, analysis, in_trait);
    let ret = if analysis.ret.typ == Default::default() {
        String::new()
    } else {
        analysis.ret.trampoline_to_glib(&env.library)
    };
    format!("f({}){}", params, ret)
}

fn trampoline_call_parameters(env: &Env, analysis: &Trampoline, in_trait: bool) -> String {
    let mut need_downcast = in_trait;
    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &analysis.parameters {
        let par_str = par.trampoline_from_glib(env, need_downcast);
        parameter_strs.push(par_str);
        need_downcast = false;  //Only downcast first parameter
    }

    parameter_strs.join(", ")
}
<|endoftext|>


<commit_before>use raw_types::{Offset16, Offset32, Uint16};

toy_table_macro::tables! {
    Gdef_1_0 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 0
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
    }

    Gdef_1_2 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 2
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
        /// Offset to the table of mark glyph set definitions, from
        /// beginning of GDEF header (may be NULL)
        mark_glyph_sets_def_offset: Offset16,
    }

    Gdef_1_3 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 3
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
        /// Offset to the table of mark glyph set definitions, from
        /// beginning of GDEF header (may be NULL)
        mark_glyph_sets_def_offset: Offset16,
        /// Offset to the Item Variation Store table, from beginning of
        /// GDEF header (may be NULL)
        item_var_store_offset: Offset32,
    }
}

toy_table_macro::tables! {
AttachList<'a> {
    /// Offset to Coverage table - from beginning of AttachList table
    coverage_offset: Offset16,
    /// Number of glyphs with attachment points
    glyph_count: Uint16,
    /// Array of offsets to AttachPoint tables-from beginning of
    /// AttachList table-in Coverage Index order
    #[count(glyph_count)]
    attach_point_offsets: [Offset16],
}

AttachPoint<'a> {
    /// Number of attachment points on this glyph
    point_count: Uint16,
    /// Array of contour point indices -in increasing numerical order
    #[count(point_count)]
    point_indices: [Uint16],
}
}

toy_table_macro::tables! {
    LigCaretList<'a> {
        /// Offset to Coverage table - from beginning of LigCaretList table
        coverage_offset: Offset16,
        /// Number of ligature glyphs
        lig_glyph_count: Uint16,
        /// Array of offsets to LigGlyph tables, from beginning of
        /// LigCaretList table —in Coverage Index order
        #[count(lig_glyph_count)]
        lig_glyph_offsets: [Offset16],
    }

    LigGlyph<'a> {
        /// Number of CaretValue tables for this ligature (components - 1)
        caret_count: Uint16,
        /// Array of offsets to CaretValue tables, from beginning of
        /// LigGlyph table — in increasing coordinate order
        #[count(caret_count)]
        caret_value_offsets: [Offset16],
    }

    CaretValueFormat1 {
        /// Format identifier: format = 1
        caret_value_format: Uint16,
        /// X or Y value, in design units
        coordinate: Int16,
    }

    CaretValueFormat2 {
        /// Format identifier: format = 2
        caret_value_format: Uint16,
        /// Contour point index on glyph
        caret_value_point_index: Uint16,
    }

    CaretValueFormat3 {
        /// Format identifier-format = 3
        caret_value_format: Uint16,
        /// X or Y value, in design units
        coordinate: Int16,
        /// Offset to Device table (non-variable font) / Variation Index
        /// table (variable font) for X or Y value-from beginning of
        /// CaretValue table
        device_offset: Offset16,
    }
}

toy_table_macro::tables! {
    MarkGlyphSets<'a> {
        /// Format identifier == 1
        format: Uint16,
        /// Number of mark glyph sets defined
        mark_glyph_set_count: Uint16,
        /// Array of offsets to mark glyph set coverage tables, from the
        /// start of the MarkGlyphSets table.
        #[count(mark_glyph_set_count)]
        coverage_offsets: [Offset32],
    }
}
<commit_msg>Add caret enum in gdef<commit_after>use raw_types::{Offset16, Offset32, Uint16};

toy_table_macro::tables! {
    Gdef_1_0 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 0
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
    }

    Gdef_1_2 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 2
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
        /// Offset to the table of mark glyph set definitions, from
        /// beginning of GDEF header (may be NULL)
        mark_glyph_sets_def_offset: Offset16,
    }

    Gdef_1_3 {
        /// Major version of the GDEF table, = 1
        major_version: Uint16,
        /// Minor version of the GDEF table, = 3
        minor_version: Uint16,
        /// Offset to class definition table for glyph type, from beginning
        /// of GDEF header (may be NULL)
        glyph_class_def_offset: Offset16,
        /// Offset to attachment point list table, from beginning of GDEF
        /// header (may be NULL)
        attach_list_offset: Offset16,
        /// Offset to ligature caret list table, from beginning of GDEF
        /// header (may be NULL)
        lig_caret_list_offset: Offset16,
        /// Offset to class definition table for mark attachment type, from
        /// beginning of GDEF header (may be NULL)
        mark_attach_class_def_offset: Offset16,
        /// Offset to the table of mark glyph set definitions, from
        /// beginning of GDEF header (may be NULL)
        mark_glyph_sets_def_offset: Offset16,
        /// Offset to the Item Variation Store table, from beginning of
        /// GDEF header (may be NULL)
        item_var_store_offset: Offset32,
    }
}

toy_table_macro::tables! {
AttachList<'a> {
    /// Offset to Coverage table - from beginning of AttachList table
    coverage_offset: Offset16,
    /// Number of glyphs with attachment points
    glyph_count: Uint16,
    /// Array of offsets to AttachPoint tables-from beginning of
    /// AttachList table-in Coverage Index order
    #[count(glyph_count)]
    attach_point_offsets: [Offset16],
}

AttachPoint<'a> {
    /// Number of attachment points on this glyph
    point_count: Uint16,
    /// Array of contour point indices -in increasing numerical order
    #[count(point_count)]
    point_indices: [Uint16],
}
}

toy_table_macro::tables! {
    LigCaretList<'a> {
        /// Offset to Coverage table - from beginning of LigCaretList table
        coverage_offset: Offset16,
        /// Number of ligature glyphs
        lig_glyph_count: Uint16,
        /// Array of offsets to LigGlyph tables, from beginning of
        /// LigCaretList table —in Coverage Index order
        #[count(lig_glyph_count)]
        lig_glyph_offsets: [Offset16],
    }

    LigGlyph<'a> {
        /// Number of CaretValue tables for this ligature (components - 1)
        caret_count: Uint16,
        /// Array of offsets to CaretValue tables, from beginning of
        /// LigGlyph table — in increasing coordinate order
        #[count(caret_count)]
        caret_value_offsets: [Offset16],
    }

    CaretValueFormat1 {
        /// Format identifier: format = 1
        caret_value_format: Uint16,
        /// X or Y value, in design units
        coordinate: Int16,
    }

    CaretValueFormat2 {
        /// Format identifier: format = 2
        caret_value_format: Uint16,
        /// Contour point index on glyph
        caret_value_point_index: Uint16,
    }

    CaretValueFormat3 {
        /// Format identifier-format = 3
        caret_value_format: Uint16,
        /// X or Y value, in design units
        coordinate: Int16,
        /// Offset to Device table (non-variable font) / Variation Index
        /// table (variable font) for X or Y value-from beginning of
        /// CaretValue table
        device_offset: Offset16,
    }

    #[format(Uint16)]
    enum CaretValue {
        #[version(CaretValue::FORMAT_1)]
        Format1(CaretValueFormat1),
        #[version(CaretValue::FORMAT_2)]
        Format2(CaretValueFormat2),
        #[version(CaretValue::FORMAT_3)]
        Format3(CaretValueFormat3),
    }
}

impl CaretValue {
    const FORMAT_1: Uint16 = Uint16::from_bytes(1u16.to_be_bytes());
    const FORMAT_2: Uint16 = Uint16::from_bytes(2u16.to_be_bytes());
    const FORMAT_3: Uint16 = Uint16::from_bytes(3u16.to_be_bytes());
}

toy_table_macro::tables! {
    MarkGlyphSets<'a> {
        /// Format identifier == 1
        format: Uint16,
        /// Number of mark glyph sets defined
        mark_glyph_set_count: Uint16,
        /// Array of offsets to mark glyph set coverage tables, from the
        /// start of the MarkGlyphSets table.
        #[count(mark_glyph_set_count)]
        coverage_offsets: [Offset32],
    }
}
<|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/. */

//! Module only available when pathfinder is activated

use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, NativeFontHandle};
use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
use pathfinder_font_renderer;
use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
use pathfinder_path_utils::cubic_to_quadratic::CubicToQuadraticTransformer;
use render_task::{RenderTask, RenderTaskTree, RenderTaskCache, RenderTaskCacheKey, RenderTaskCacheEntryHandle,
                  RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard};
use tiling::{RenderTargetKind, SpecialRenderPasses};
use glyph_rasterizer::AddFont;
use internal_types::ResourceCacheError;
use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
use std::collections::hash_map::Entry;
use std::f32;
use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts};
use texture_cache::TextureCache;
use gpu_cache::GpuCache;
use profiler::TextureCacheProfileCounters;

/// Should match macOS 10.13 High Sierra.
///
/// We multiply by sqrt(2) to compensate for the fact that dilation amounts are relative to the
/// pixel square on macOS and relative to the vertex normal in Pathfinder.
const STEM_DARKENING_FACTOR_X: f32 = 0.0121 * f32::consts::SQRT_2;
const STEM_DARKENING_FACTOR_Y: f32 = 0.0121 * 1.25 * f32::consts::SQRT_2;

/// Likewise, should match macOS 10.13 High Sierra.
const MAX_STEM_DARKENING_AMOUNT: f32 = 0.3 * f32::consts::SQRT_2;

const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 0.01;

type PathfinderFontContext = pathfinder_font_renderer::FontContext<FontKey>;

impl AddFont for PathfinderFontContext {
    fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
        match *template {
            FontTemplate::Raw(ref bytes, index) => {
                drop(self.add_font_from_memory(&font_key, bytes.clone(), index))
            }
            FontTemplate::Native(ref native_font_handle) => {
                drop(self.add_native_font(&font_key, NativeFontHandleWrapper(native_font_handle)))
            }
        }
    }
}

pub(in super) fn create_pathfinder_font_context()
-> Result<Box<ThreadSafePathfinderFontContext>, ResourceCacheError>
{
    match PathfinderFontContext::new() {
        Ok(context) => Ok(Box::new(ThreadSafePathfinderFontContext(Mutex::new(context)))),
        Err(_) => {
            let msg = "Failed to create the Pathfinder font context!".to_owned();
            Err(ResourceCacheError::new(msg))
        }
    }
}

pub struct ThreadSafePathfinderFontContext(Mutex<PathfinderFontContext>);

impl Deref for ThreadSafePathfinderFontContext {
    type Target = Mutex<PathfinderFontContext>;

    fn deref(&self) -> &Mutex<PathfinderFontContext> {
        &self.0
    }
}

/// PathfinderFontContext can contain a *mut IDWriteFactory.
/// However, since we know that it is wrapped in a Mutex, it is safe
/// to assume that this struct is thread-safe
unsafe impl Send for ThreadSafePathfinderFontContext {}
unsafe impl Sync for ThreadSafePathfinderFontContext { }

impl GlyphRasterizer {

    pub(in super) fn add_font_to_pathfinder(&mut self, font_key: &FontKey, template: &FontTemplate) {
        let font_contexts = Arc::clone(&self.font_contexts);
        debug!("add_font_to_pathfinder({:?})", font_key);
        font_contexts.lock_pathfinder_context().add_font(&font_key, &template);
    }

    pub fn get_cache_item_for_glyph(
        &self,
        glyph_key: &GlyphKey,
        font: &FontInstance,
        glyph_cache: &GlyphCache,
        texture_cache: &TextureCache,
        render_task_cache: &RenderTaskCache)
    -> Option<(CacheItem, GlyphFormat)>
    {
        let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font(font);
        let render_task_cache_key = match *glyph_key_cache.get(glyph_key) {
            GlyphCacheEntry::Cached(ref cached_glyph) => {
                (*cached_glyph).render_task_cache_key.clone()
            }
            GlyphCacheEntry::Blank => return None,
            GlyphCacheEntry::Pending => {
                panic!("GlyphRasterizer::get_cache_item_for_glyph(): Glyph should have been \
                        cached by now!")
            }
        };
        let cache_item = render_task_cache.get_cache_item_for_render_task(texture_cache,
                                                                          &render_task_cache_key);
        Some((cache_item, font.get_glyph_format()))
    }

    pub(in super) fn request_glyph_from_pathfinder_if_necessary(
        &mut self,
        glyph_key: &GlyphKey,
        font: &FontInstance,
        cached_glyph_info: CachedGlyphInfo,
        texture_cache: &mut TextureCache,
        gpu_cache: &mut GpuCache,
        render_task_cache: &mut RenderTaskCache,
        render_task_tree: &mut RenderTaskTree,
        render_passes: &mut SpecialRenderPasses)
    -> Result<(RenderTaskCacheEntryHandle,GlyphFormat), ()>
    {
        let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
        let render_task_cache_key = cached_glyph_info.render_task_cache_key;
        let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
        let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, 1.0];
        let handle = try!(render_task_cache.request_render_task(render_task_cache_key,
                                                                texture_cache,
                                                                gpu_cache,
                                                                render_task_tree,
                                                                Some(user_data),
                                                                false,
                                                                |render_tasks| {
            // TODO(pcwalton): Non-subpixel font render mode.
            request_render_task_from_pathfinder(glyph_key,
                                                font,
                                                &glyph_origin,
                                                &glyph_size,
                                                &mut *pathfinder_font_context,
                                                font.render_mode,
                                                render_tasks,
                                                render_passes)
        }));
        Ok((handle, font.get_glyph_format()))
    }

    pub fn request_glyphs(
        &mut self,
        glyph_cache: &mut GlyphCache,
        font: FontInstance,
        glyph_keys: &[GlyphKey],
        texture_cache: &mut TextureCache,
        gpu_cache: &mut GpuCache,
        render_task_cache: &mut RenderTaskCache,
        render_task_tree: &mut RenderTaskTree,
        render_passes: &mut SpecialRenderPasses,
    ) {
        debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));

        let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());

        // select glyphs that have not been requested yet.
        for glyph_key in glyph_keys {
            let mut cached_glyph_info = None;
            match glyph_key_cache.entry(glyph_key.clone()) {
                Entry::Occupied(mut entry) => {
                    let value = entry.into_mut();
                    match *value {
                        GlyphCacheEntry::Cached(ref glyph_info) => {
                            cached_glyph_info = Some(glyph_info.clone())
                        }
                        GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => {}
                    }
                }
                Entry::Vacant(_) => {}
            }

            let cached_glyph_info = match cached_glyph_info {
                Some(cached_glyph_info) => cached_glyph_info,
                None => {
                    let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();

                    let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
                        font_key: font.font_key.clone(),
                        size: font.size,
                    };

                    // TODO: pathfinder will need to support 2D subpixel offset
                    let pathfinder_subpixel_offset =
                        pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
                    let pathfinder_glyph_key =
                        pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                                pathfinder_subpixel_offset);
                    let glyph_dimensions =
                        match pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
                                                                       &pathfinder_glyph_key,
                                                                       false) {
                            Ok(glyph_dimensions) => glyph_dimensions,
                            Err(_) => continue,
                        };

                    let cached_glyph_info = CachedGlyphInfo {
                        render_task_cache_key: RenderTaskCacheKey {
                            size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
                            kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
                        },
                        format: font.get_glyph_format(),
                        origin: DeviceIntPoint::new(glyph_dimensions.origin.x as i32,
                                                    -glyph_dimensions.origin.y as i32),
                    };
                    self.next_gpu_glyph_cache_key.0 += 1;
                    cached_glyph_info
                }
            };

            let handle =
                match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
                                                                      &font,
                                                                      cached_glyph_info.clone(),
                                                                      texture_cache,
                                                                      gpu_cache,
                                                                      render_task_cache,
                                                                      render_task_tree,
                                                                      render_passes) {
                    Ok(_) => GlyphCacheEntry::Cached(cached_glyph_info),
                    Err(_) => GlyphCacheEntry::Blank,
                };

            glyph_key_cache.insert(glyph_key.clone(), handle);
        }
    }

    pub fn resolve_glyphs(
        &mut self,
        _: &mut GlyphCache,
        _: &mut TextureCache,
        _: &mut GpuCache,
        _: &mut RenderTaskCache,
        _: &mut RenderTaskTree,
        _: &mut TextureCacheProfileCounters,
    ) {
        self.remove_dead_fonts();
    }
}

impl FontContexts {
    pub fn lock_pathfinder_context(&self) -> MutexGuard<PathfinderFontContext> {
        self.pathfinder_context.lock().unwrap()
    }
}

fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
    TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
                       f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
}

fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
                                       font: &FontInstance,
                                       glyph_origin: &DeviceIntPoint,
                                       glyph_size: &DeviceIntSize,
                                       font_context: &mut PathfinderFontContext,
                                       render_mode: FontRenderMode,
                                       render_tasks: &mut RenderTaskTree,
                                       render_passes: &mut SpecialRenderPasses)
                                       -> Result<RenderTaskId, ()> {
    let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
        font_key: font.font_key.clone(),
        size: font.size,
    };

    // TODO: pathfinder will need to support 2D subpixel offset
    let pathfinder_subpixel_offset =
        pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
    let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
    let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                                       pathfinder_subpixel_offset);

    // TODO(pcwalton): Fall back to CPU rendering if Pathfinder fails to collect the outline.
    let mut mesh = PathfinderMesh::new();
    let outline = try!(font_context.glyph_outline(&pathfinder_font_instance,
                                                  &pathfinder_glyph_key));
    let tolerance = CUBIC_TO_QUADRATIC_APPROX_TOLERANCE;
    mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
    mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));

    // FIXME(pcwalton): Support vertical subpixel offsets.
    // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
    // smoothing" is unchecked in System Preferences.

    let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
    let embolden_amount = compute_embolden_amount(font.size.to_f32_px());

    let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size));
    let glyph_render_task = RenderTask::new_glyph(location,
                                                  mesh,
                                                  &glyph_origin,
                                                  &subpixel_offset,
                                                  font.render_mode,
                                                  &embolden_amount);

    let root_task_id = render_tasks.add(glyph_render_task);
    let render_pass = match render_mode {
        FontRenderMode::Mono | FontRenderMode::Alpha => &mut render_passes.alpha_glyph_pass,
        FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
    };
    render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);

    Ok(root_task_id)
}

pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
<commit_msg>Auto merge of #2790 - pcwalton:pathfinder-crash-fix, r=gw3583<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/. */

//! Module only available when pathfinder is activated

use api::{DeviceIntPoint, DeviceIntSize, DevicePixel, FontRenderMode, FontKey, FontTemplate, NativeFontHandle};
use euclid::{TypedPoint2D, TypedSize2D, TypedVector2D};
use pathfinder_font_renderer;
use pathfinder_partitioner::mesh::Mesh as PathfinderMesh;
use pathfinder_path_utils::cubic_to_quadratic::CubicToQuadraticTransformer;
use render_task::{RenderTask, RenderTaskTree, RenderTaskCache, RenderTaskCacheKey, RenderTaskCacheEntryHandle,
                  RenderTaskCacheKeyKind, RenderTaskId, RenderTaskLocation};
use resource_cache::CacheItem;
use std::ops::Deref;
use std::sync::{Arc, Mutex, MutexGuard};
use tiling::{RenderTargetKind, SpecialRenderPasses};
use glyph_rasterizer::AddFont;
use internal_types::ResourceCacheError;
use glyph_cache::{GlyphCache, GlyphCacheEntry, CachedGlyphInfo};
use std::collections::hash_map::Entry;
use std::f32;
use glyph_rasterizer::{FontInstance, GlyphRasterizer, GlyphFormat, GlyphKey, FontContexts};
use texture_cache::TextureCache;
use gpu_cache::GpuCache;
use profiler::TextureCacheProfileCounters;

/// Should match macOS 10.13 High Sierra.
///
/// We multiply by sqrt(2) to compensate for the fact that dilation amounts are relative to the
/// pixel square on macOS and relative to the vertex normal in Pathfinder.
const STEM_DARKENING_FACTOR_X: f32 = 0.0121 * f32::consts::SQRT_2;
const STEM_DARKENING_FACTOR_Y: f32 = 0.0121 * 1.25 * f32::consts::SQRT_2;

/// Likewise, should match macOS 10.13 High Sierra.
const MAX_STEM_DARKENING_AMOUNT: f32 = 0.3 * f32::consts::SQRT_2;

const CUBIC_TO_QUADRATIC_APPROX_TOLERANCE: f32 = 0.01;

type PathfinderFontContext = pathfinder_font_renderer::FontContext<FontKey>;

impl AddFont for PathfinderFontContext {
    fn add_font(&mut self, font_key: &FontKey, template: &FontTemplate) {
        match *template {
            FontTemplate::Raw(ref bytes, index) => {
                drop(self.add_font_from_memory(&font_key, bytes.clone(), index))
            }
            FontTemplate::Native(ref native_font_handle) => {
                drop(self.add_native_font(&font_key, NativeFontHandleWrapper(native_font_handle)))
            }
        }
    }
}

pub(in super) fn create_pathfinder_font_context()
-> Result<Box<ThreadSafePathfinderFontContext>, ResourceCacheError>
{
    match PathfinderFontContext::new() {
        Ok(context) => Ok(Box::new(ThreadSafePathfinderFontContext(Mutex::new(context)))),
        Err(_) => {
            let msg = "Failed to create the Pathfinder font context!".to_owned();
            Err(ResourceCacheError::new(msg))
        }
    }
}

pub struct ThreadSafePathfinderFontContext(Mutex<PathfinderFontContext>);

impl Deref for ThreadSafePathfinderFontContext {
    type Target = Mutex<PathfinderFontContext>;

    fn deref(&self) -> &Mutex<PathfinderFontContext> {
        &self.0
    }
}

/// PathfinderFontContext can contain a *mut IDWriteFactory.
/// However, since we know that it is wrapped in a Mutex, it is safe
/// to assume that this struct is thread-safe
unsafe impl Send for ThreadSafePathfinderFontContext {}
unsafe impl Sync for ThreadSafePathfinderFontContext { }

impl GlyphRasterizer {
    pub(in super) fn add_font_to_pathfinder(&mut self, font_key: &FontKey, template: &FontTemplate) {
        let font_contexts = Arc::clone(&self.font_contexts);
        debug!("add_font_to_pathfinder({:?})", font_key);
        font_contexts.lock_pathfinder_context().add_font(&font_key, &template);
    }

    pub fn get_cache_item_for_glyph(
        &self,
        glyph_key: &GlyphKey,
        font: &FontInstance,
        glyph_cache: &GlyphCache,
        texture_cache: &TextureCache,
        render_task_cache: &RenderTaskCache)
    -> Option<(CacheItem, GlyphFormat)>
    {
        let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font(font);
        let render_task_cache_key = match *glyph_key_cache.get(glyph_key) {
            GlyphCacheEntry::Cached(ref cached_glyph) => {
                (*cached_glyph).render_task_cache_key.clone()
            }
            GlyphCacheEntry::Blank => return None,
            GlyphCacheEntry::Pending => {
                panic!("GlyphRasterizer::get_cache_item_for_glyph(): Glyph should have been \
                        cached by now!")
            }
        };
        let cache_item = render_task_cache.get_cache_item_for_render_task(texture_cache,
                                                                          &render_task_cache_key);
        Some((cache_item, font.get_glyph_format()))
    }

    pub(in super) fn request_glyph_from_pathfinder_if_necessary(
        &mut self,
        glyph_key: &GlyphKey,
        font: &FontInstance,
        cached_glyph_info: CachedGlyphInfo,
        texture_cache: &mut TextureCache,
        gpu_cache: &mut GpuCache,
        render_task_cache: &mut RenderTaskCache,
        render_task_tree: &mut RenderTaskTree,
        render_passes: &mut SpecialRenderPasses)
    -> Result<(RenderTaskCacheEntryHandle,GlyphFormat), ()>
    {
        let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();
        let render_task_cache_key = cached_glyph_info.render_task_cache_key;
        let (glyph_origin, glyph_size) = (cached_glyph_info.origin, render_task_cache_key.size);
        let user_data = [glyph_origin.x as f32, (glyph_origin.y - glyph_size.height) as f32, 1.0];
        let handle = try!(render_task_cache.request_render_task(render_task_cache_key,
                                                                texture_cache,
                                                                gpu_cache,
                                                                render_task_tree,
                                                                Some(user_data),
                                                                false,
                                                                |render_tasks| {
            // TODO(pcwalton): Non-subpixel font render mode.
            request_render_task_from_pathfinder(glyph_key,
                                                font,
                                                &glyph_origin,
                                                &glyph_size,
                                                &mut *pathfinder_font_context,
                                                font.render_mode,
                                                render_tasks,
                                                render_passes)
        }));
        Ok((handle, font.get_glyph_format()))
    }

    pub fn request_glyphs(
        &mut self,
        glyph_cache: &mut GlyphCache,
        font: FontInstance,
        glyph_keys: &[GlyphKey],
        texture_cache: &mut TextureCache,
        gpu_cache: &mut GpuCache,
        render_task_cache: &mut RenderTaskCache,
        render_task_tree: &mut RenderTaskTree,
        render_passes: &mut SpecialRenderPasses,
    ) {
        debug_assert!(self.font_contexts.lock_shared_context().has_font(&font.font_key));

        let glyph_key_cache = glyph_cache.get_glyph_key_cache_for_font_mut(font.clone());

        // select glyphs that have not been requested yet.
        for glyph_key in glyph_keys {
            let mut cached_glyph_info = None;
            match glyph_key_cache.entry(glyph_key.clone()) {
                Entry::Occupied(mut entry) => {
                    let value = entry.into_mut();
                    match *value {
                        GlyphCacheEntry::Cached(ref glyph_info) => {
                            cached_glyph_info = Some(glyph_info.clone())
                        }
                        GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => {}
                    }
                }
                Entry::Vacant(_) => {}
            }

            if cached_glyph_info.is_none() {
                let mut pathfinder_font_context = self.font_contexts.lock_pathfinder_context();

                let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
                    font_key: font.font_key.clone(),
                    size: font.size,
                };

                // TODO: pathfinder will need to support 2D subpixel offset
                let pathfinder_subpixel_offset =
                    pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
                let pathfinder_glyph_key =
                    pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                            pathfinder_subpixel_offset);

                if let Ok(glyph_dimensions) =
                        pathfinder_font_context.glyph_dimensions(&pathfinder_font_instance,
                                                                 &pathfinder_glyph_key,
                                                                 false) {
                    let render_task_cache_key = RenderTaskCacheKey {
                        size: TypedSize2D::from_untyped(&glyph_dimensions.size.to_i32()),
                        kind: RenderTaskCacheKeyKind::Glyph(self.next_gpu_glyph_cache_key),
                    };
                    cached_glyph_info = Some(CachedGlyphInfo {
                        render_task_cache_key,
                        format: font.get_glyph_format(),
                        origin: DeviceIntPoint::new(glyph_dimensions.origin.x as i32,
                                                    -glyph_dimensions.origin.y as i32),
                    });
                    self.next_gpu_glyph_cache_key.0 += 1;
                }
            }

            let handle = match cached_glyph_info {
                Some(glyph_info) => {
                    match self.request_glyph_from_pathfinder_if_necessary(glyph_key,
                                                                          &font,
                                                                          glyph_info.clone(),
                                                                          texture_cache,
                                                                          gpu_cache,
                                                                          render_task_cache,
                                                                          render_task_tree,
                                                                          render_passes) {
                        Ok(_) => GlyphCacheEntry::Cached(glyph_info),
                        Err(_) => GlyphCacheEntry::Blank,
                    }
                }
                None => GlyphCacheEntry::Blank,
            };

            glyph_key_cache.insert(glyph_key.clone(), handle);
        }
    }

    pub fn resolve_glyphs(
        &mut self,
        _: &mut GlyphCache,
        _: &mut TextureCache,
        _: &mut GpuCache,
        _: &mut RenderTaskCache,
        _: &mut RenderTaskTree,
        _: &mut TextureCacheProfileCounters,
    ) {
        self.remove_dead_fonts();
    }
}

impl FontContexts {
    pub fn lock_pathfinder_context(&self) -> MutexGuard<PathfinderFontContext> {
        self.pathfinder_context.lock().unwrap()
    }
}

fn compute_embolden_amount(ppem: f32) -> TypedVector2D<f32, DevicePixel> {
    TypedVector2D::new(f32::min(ppem * STEM_DARKENING_FACTOR_X, MAX_STEM_DARKENING_AMOUNT),
                       f32::min(ppem * STEM_DARKENING_FACTOR_Y, MAX_STEM_DARKENING_AMOUNT))
}

fn request_render_task_from_pathfinder(glyph_key: &GlyphKey,
                                       font: &FontInstance,
                                       glyph_origin: &DeviceIntPoint,
                                       glyph_size: &DeviceIntSize,
                                       font_context: &mut PathfinderFontContext,
                                       render_mode: FontRenderMode,
                                       render_tasks: &mut RenderTaskTree,
                                       render_passes: &mut SpecialRenderPasses)
                                       -> Result<RenderTaskId, ()> {
    let pathfinder_font_instance = pathfinder_font_renderer::FontInstance {
        font_key: font.font_key.clone(),
        size: font.size,
    };

    // TODO: pathfinder will need to support 2D subpixel offset
    let pathfinder_subpixel_offset =
        pathfinder_font_renderer::SubpixelOffset(glyph_key.subpixel_offset.0 as u8);
    let glyph_subpixel_offset: f64 = glyph_key.subpixel_offset.0.into();
    let pathfinder_glyph_key = pathfinder_font_renderer::GlyphKey::new(glyph_key.index,
                                                                       pathfinder_subpixel_offset);

    // TODO(pcwalton): Fall back to CPU rendering if Pathfinder fails to collect the outline.
    let mut mesh = PathfinderMesh::new();
    let outline = try!(font_context.glyph_outline(&pathfinder_font_instance,
                                                  &pathfinder_glyph_key));
    let tolerance = CUBIC_TO_QUADRATIC_APPROX_TOLERANCE;
    mesh.push_stencil_segments(CubicToQuadraticTransformer::new(outline.iter(), tolerance));
    mesh.push_stencil_normals(CubicToQuadraticTransformer::new(outline.iter(), tolerance));

    // FIXME(pcwalton): Support vertical subpixel offsets.
    // FIXME(pcwalton): Embolden amount should be 0 on macOS if "Use LCD font
    // smoothing" is unchecked in System Preferences.

    let subpixel_offset = TypedPoint2D::new(glyph_subpixel_offset as f32, 0.0);
    let embolden_amount = compute_embolden_amount(font.size.to_f32_px());

    let location = RenderTaskLocation::Dynamic(None, Some(*glyph_size));
    let glyph_render_task = RenderTask::new_glyph(location,
                                                  mesh,
                                                  &glyph_origin,
                                                  &subpixel_offset,
                                                  font.render_mode,
                                                  &embolden_amount);

    let root_task_id = render_tasks.add(glyph_render_task);
    let render_pass = match render_mode {
        FontRenderMode::Mono | FontRenderMode::Alpha => &mut render_passes.alpha_glyph_pass,
        FontRenderMode::Subpixel => &mut render_passes.color_glyph_pass,
    };
    render_pass.add_render_task(root_task_id, *glyph_size, RenderTargetKind::Color);

    Ok(root_task_id)
}

pub struct NativeFontHandleWrapper<'a>(pub &'a NativeFontHandle);
<|endoftext|>


<commit_before>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod args;
mod logger;

use std::{convert::TryFrom, env, fs, path::PathBuf, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{cli, config::Config, from_json, Result};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if let Err(err) = try_main() {
        log::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let args = args::Args::parse()?;

    #[cfg(debug_assertions)]
    if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    setup_logging(args.log_file, args.no_buffering)?;
    match args.command {
        args::Command::RunServer => run_server()?,
        args::Command::PrintConfigSchema => {
            println!("{:#}", Config::json_schema());
        }
        args::Command::ProcMacro => proc_macro_srv::cli::run()?,

        args::Command::Parse { no_dump } => cli::parse(no_dump)?,
        args::Command::Symbols => cli::symbols()?,
        args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
        args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?,
        args::Command::Bench(cmd) => cmd.run(args.verbosity)?,
        args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => {
            cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)?
        }
        args::Command::Ssr { rules } => {
            cli::apply_ssr_rules(rules)?;
        }
        args::Command::StructuredSearch { patterns, debug_snippet } => {
            cli::search_for_patterns(patterns, debug_snippet)?;
        }
        args::Command::Version => println!("rust-analyzer {}", env!("REV")),
        args::Command::Help => {}
    }
    Ok(())
}

fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> {
    env::set_var("RUST_BACKTRACE", "short");

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();

    tracing_setup::setup_tracing()?;

    profile::init();

    if !cfg!(debug_assertions) {
        stdx::set_assert_hook(|loc, args| {
            if env::var("RA_PROFILE").is_ok() {
                panic!("assertion failed at {}: {}", loc, args)
            }
            log::error!("assertion failed at {}: {}", loc, args)
        });
    }

    Ok(())
}

mod tracing_setup {
    use tracing::subscriber;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::EnvFilter;
    use tracing_subscriber::Registry;
    use tracing_tree::HierarchicalLayer;

    pub(crate) fn setup_tracing() -> super::Result<()> {
        let filter = EnvFilter::from_env("CHALK_DEBUG");
        let layer = HierarchicalLayer::default()
            .with_indent_lines(true)
            .with_ansi(false)
            .with_indent_amount(2)
            .with_writer(std::io::stderr);
        let subscriber = Registry::default().with(filter).with(layer);
        subscriber::set_global_default(subscriber)?;
        Ok(())
    }
}

fn run_server() -> Result<()> {
    log::info!("server will start");

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    log::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    let config = {
        let root_path = match initialize_params
            .root_uri
            .and_then(|it| it.to_file_path().ok())
            .and_then(|it| AbsPathBuf::try_from(it).ok())
        {
            Some(it) => it,
            None => {
                let cwd = env::current_dir()?;
                AbsPathBuf::assert(cwd)
            }
        };

        let mut config = Config::new(root_path, initialize_params.capabilities);
        if let Some(json) = initialize_params.initialization_options {
            config.update(json);
        }

        if config.linked_projects().is_empty() {
            let workspace_roots = initialize_params
                .workspace_folders
                .map(|workspaces| {
                    workspaces
                        .into_iter()
                        .filter_map(|it| it.uri.to_file_path().ok())
                        .filter_map(|it| AbsPathBuf::try_from(it).ok())
                        .collect::<Vec<_>>()
                })
                .filter(|workspaces| !workspaces.is_empty())
                .unwrap_or_else(|| vec![config.root_path.clone()]);

            let discovered = ProjectManifest::discover_all(&workspace_roots);
            log::info!("discovered projects: {:?}", discovered);
            if discovered.is_empty() {
                log::error!("failed to find any projects in {:?}", workspace_roots);
            }

            config.discovered_projects = Some(discovered);
        }

        config
    };

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    log::info!("server did shut down");
    Ok(())
}
<commit_msg>cargo fmt<commit_after>//! Driver for rust-analyzer.
//!
//! Based on cli flags, either spawns an LSP server, or runs a batch analysis
mod args;
mod logger;

use std::{convert::TryFrom, env, fs, path::PathBuf, process};

use lsp_server::Connection;
use project_model::ProjectManifest;
use rust_analyzer::{cli, config::Config, from_json, Result};
use vfs::AbsPathBuf;

#[cfg(all(feature = "mimalloc"))]
#[global_allocator]
static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc;

#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

fn main() {
    if let Err(err) = try_main() {
        log::error!("Unexpected error: {}", err);
        eprintln!("{}", err);
        process::exit(101);
    }
}

fn try_main() -> Result<()> {
    let args = args::Args::parse()?;

    #[cfg(debug_assertions)]
    if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
        #[allow(unused_mut)]
        let mut d = 4;
        while d == 4 {
            d = 4;
        }
    }

    setup_logging(args.log_file, args.no_buffering)?;
    match args.command {
        args::Command::RunServer => run_server()?,
        args::Command::PrintConfigSchema => {
            println!("{:#}", Config::json_schema());
        }
        args::Command::ProcMacro => proc_macro_srv::cli::run()?,

        args::Command::Parse { no_dump } => cli::parse(no_dump)?,
        args::Command::Symbols => cli::symbols()?,
        args::Command::Highlight { rainbow } => cli::highlight(rainbow)?,
        args::Command::AnalysisStats(cmd) => cmd.run(args.verbosity)?,
        args::Command::Bench(cmd) => cmd.run(args.verbosity)?,
        args::Command::Diagnostics { path, load_output_dirs, with_proc_macro } => {
            cli::diagnostics(path.as_ref(), load_output_dirs, with_proc_macro)?
        }
        args::Command::Ssr { rules } => {
            cli::apply_ssr_rules(rules)?;
        }
        args::Command::StructuredSearch { patterns, debug_snippet } => {
            cli::search_for_patterns(patterns, debug_snippet)?;
        }
        args::Command::Version => println!("rust-analyzer {}", env!("REV")),
        args::Command::Help => {}
    }
    Ok(())
}

fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> {
    env::set_var("RUST_BACKTRACE", "short");

    let log_file = match log_file {
        Some(path) => {
            if let Some(parent) = path.parent() {
                let _ = fs::create_dir_all(parent);
            }
            Some(fs::File::create(path)?)
        }
        None => None,
    };
    let filter = env::var("RA_LOG").ok();
    logger::Logger::new(log_file, no_buffering, filter.as_deref()).install();

    tracing_setup::setup_tracing()?;

    profile::init();

    if !cfg!(debug_assertions) {
        stdx::set_assert_hook(|loc, args| {
            if env::var("RA_PROFILE").is_ok() {
                panic!("assertion failed at {}: {}", loc, args)
            }
            log::error!("assertion failed at {}: {}", loc, args)
        });
    }

    Ok(())
}

mod tracing_setup {
    use tracing::subscriber;
    use tracing_subscriber::layer::SubscriberExt;
    use tracing_subscriber::EnvFilter;
    use tracing_subscriber::Registry;
    use tracing_tree::HierarchicalLayer;

    pub(crate) fn setup_tracing() -> super::Result<()> {
        let filter = EnvFilter::from_env("CHALK_DEBUG");
        let layer = HierarchicalLayer::default()
            .with_indent_lines(true)
            .with_ansi(false)
            .with_indent_amount(2)
            .with_writer(std::io::stderr);
        let subscriber = Registry::default().with(filter).with(layer);
        subscriber::set_global_default(subscriber)?;
        Ok(())
    }
}

fn run_server() -> Result<()> {
    log::info!("server will start");

    let (connection, io_threads) = Connection::stdio();

    let (initialize_id, initialize_params) = connection.initialize_start()?;
    log::info!("InitializeParams: {}", initialize_params);
    let initialize_params =
        from_json::<lsp_types::InitializeParams>("InitializeParams", initialize_params)?;

    let server_capabilities = rust_analyzer::server_capabilities(&initialize_params.capabilities);

    let initialize_result = lsp_types::InitializeResult {
        capabilities: server_capabilities,
        server_info: Some(lsp_types::ServerInfo {
            name: String::from("rust-analyzer"),
            version: Some(String::from(env!("REV"))),
        }),
    };

    let initialize_result = serde_json::to_value(initialize_result).unwrap();

    connection.initialize_finish(initialize_id, initialize_result)?;

    if let Some(client_info) = initialize_params.client_info {
        log::info!("Client '{}' {}", client_info.name, client_info.version.unwrap_or_default());
    }

    let config = {
        let root_path = match initialize_params
            .root_uri
            .and_then(|it| it.to_file_path().ok())
            .and_then(|it| AbsPathBuf::try_from(it).ok())
        {
            Some(it) => it,
            None => {
                let cwd = env::current_dir()?;
                AbsPathBuf::assert(cwd)
            }
        };

        let mut config = Config::new(root_path, initialize_params.capabilities);
        if let Some(json) = initialize_params.initialization_options {
            config.update(json);
        }

        if config.linked_projects().is_empty() {
            let workspace_roots = initialize_params
                .workspace_folders
                .map(|workspaces| {
                    workspaces
                        .into_iter()
                        .filter_map(|it| it.uri.to_file_path().ok())
                        .filter_map(|it| AbsPathBuf::try_from(it).ok())
                        .collect::<Vec<_>>()
                })
                .filter(|workspaces| !workspaces.is_empty())
                .unwrap_or_else(|| vec![config.root_path.clone()]);

            let discovered = ProjectManifest::discover_all(&workspace_roots);
            log::info!("discovered projects: {:?}", discovered);
            if discovered.is_empty() {
                log::error!("failed to find any projects in {:?}", workspace_roots);
            }

            config.discovered_projects = Some(discovered);
        }

        config
    };

    rust_analyzer::main_loop(config, connection)?;

    io_threads.join()?;
    log::info!("server did shut down");
    Ok(())
}
<|endoftext|>


<commit_before>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::collections::HashMap;
use std::io::{BufferedReader, RefReader, IoResult, IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    let mut p = try!(c.spawn());
    p.set_timeout(timeout);
    Ok(p)
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
pub fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    (try!(
        (try!(
            spawn_with_timeout(c, timeout)))
            .wait())).if_ok(on_success)
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

#[deriving(Show, PartialEq)]
pub enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command, timeout: Option<u64>) -> IoResult<ProcessReader> {
        spawn_with_timeout(cmd, timeout).map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            Reader::by_ref(
                self.p.stdout.as_mut().unwrap()))
    }
}

fn parse_test_result(line: &str) -> IoResult<TestResult> {
    match line {
        "PASS" => Ok(Pass),
        "FAIL" => Ok(Fail),
        _ => Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test result",
                detail: Some(line.to_string())
            })
    }
}

fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
    let results: Vec<&str> = line.split_str(":").collect();
    if results.len() == 2 {
        parse_test_result(results[1]).map(|res| {
            (results[0].to_string(), res)
        })
    } else {
        Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test string",
                detail: Some(line.to_string())
            })
    }
}

#[deriving(Show)]
pub enum BuildResult {
    SetupEnvFailure(IoError),
    BuildFailure(IoError),
    TestFailure(IoError),
    TestSuccess(HashMap<String, TestResult>)
}

pub trait WholeBuildable {
    // BEGIN FUNCTIONS TO IMPLEMENT
    fn env_timeout(&self) -> Option<u64>;
    fn env_command(&self) -> Command;

    fn build_timeout(&self) -> Option<u64>;
    fn build_command(&self) -> Command;

    fn test_timeout(&self) -> Option<u64>;
    fn test_command(&self) -> Command;
    // END FUNCTIONS TO IMPLEMENT

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<()> {
        run_command(&self.env_command(), self.env_timeout(), ())
    }

    fn do_build(&self) -> IoResult<()> {
        run_command(&self.build_command(), self.build_timeout(), ())
    }

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        let mut reader =
            try!(ProcessReader::new(&self.test_command(), self.test_timeout()));
        let mut map = HashMap::new();
        for op_line in reader.output_reader().lines() {
            let (k, v) = try!(
                parse_line(
                    try!(op_line).as_slice().trim()));
            map.insert(k, v);
        }

        Ok(map)
    }

    fn whole_build(&self) -> BuildResult {
        // Because we have different results for different kinds
        // of failures, we cannot use `try!`
        match self.setup_env() {
            Ok(_) => {
                match self.do_build() {
                    Ok(_) => {
                        match self.do_testing() {
                            Ok(res) => TestSuccess(res),
                            Err(e) => TestFailure(e)
                        }
                    },
                    Err(e) => BuildFailure(e)
                }
            },
            Err(e) => SetupEnvFailure(e)
        }
    }
}

pub trait ToWholeBuildable<A : WholeBuildable> {
    fn to_whole_buildable(&self) -> A;
}

#[cfg(test)]
mod process_tests {
    use std::io::process::Command;
    use super::{run_command, ProcessReader};

    #[test]
    fn echo_ok() {
        assert!(run_command(&*Command::new("echo").arg("foobar"), None, ()).is_ok());
    }

    #[test]
    fn false_ok() {
        assert!(run_command(&Command::new("false"), None, ()).is_err());
    }

    fn output_from_command(cmd: &Command) -> Vec<String> {
        let pr = ProcessReader::new(cmd, None);
        assert!(pr.is_ok());
        pr.unwrap().output_reader().lines()
            .map(|line| {
                assert!(line.is_ok());
                line.unwrap().as_slice().trim().to_string()
            }).collect()
    }

    #[test]
    fn output_single_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foobar"));
        assert_eq!(lines.len(), 1);
        assert_eq!(lines[0].as_slice(), "foobar");
    }

    #[test]
    fn output_multi_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foo; echo bar"));
        assert_eq!(lines.len(), 2);
        assert_eq!(lines[0].as_slice(), "foo");
        assert_eq!(lines[1].as_slice(), "bar");
    }
}

#[cfg(test)]
mod parse_tests {
    use super::{parse_test_result, Pass, Fail, parse_line};

    #[test]
    fn parse_test_pass() {
        let res = parse_test_result("PASS");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Pass);
    }

    #[test]
    fn parse_test_fail() {
        let res = parse_test_result("FAIL");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Fail);
    }

    #[test]
    fn parse_test_bad_test() {
        let res = parse_test_result("foobar");
        assert!(res.is_err());
    }

    #[test]
    fn parse_valid_test_line() {
        let res = parse_line("my test:PASS");
        assert!(res.is_ok());
        let (key, result) = res.unwrap();
        assert_eq!(key.as_slice(), "my test");
        assert_eq!(result, Pass);
    }

    #[test]
    fn parse_invalid_test_line() {
        assert!(parse_line("this:is:PASS").is_err());
    }
}

pub mod testing {
    use std::io::process::Command;
    use std::path::BytesContainer;

    use super::{run_command, WholeBuildable};

    pub struct TestingRequest {
        dir: Path, // directory where the build is to be performed
        makefile_loc: Path // where the makefile is located
    }

    impl TestingRequest {
        pub fn new<A : BytesContainer>(dir: A, makefile_loc: A) -> TestingRequest {
            TestingRequest {
                dir: Path::new(dir),
                makefile_loc: Path::new(makefile_loc)
            }
        }

        fn make_with_arg<A : ToCStr>(&self, arg: A) -> Command {
            let mut c = Command::new("make");
            c.arg("-s").arg(arg).cwd(&self.dir);
            c
        }
    }

    impl Drop for TestingRequest {
        /// Automatically deletes the copied-over makefile on test end,
        /// along with any applicable executables (namely `a.out`)
        #[allow(unused_must_use)]
        fn drop(&mut self) {
            let dir = self.dir.as_str().unwrap();
            run_command(
                &*Command::new("rm")
                    .arg(format!("{}/makefile", dir))
                    .arg(format!("{}/a.out", dir)),
                None,
                ());
        }
    }

    impl WholeBuildable for TestingRequest {
        fn env_timeout(&self) -> Option<u64> { None }

        fn env_command(&self) -> Command {
            let mut c = Command::new("cp");
            c.arg(self.makefile_loc.as_str().unwrap());
            c.arg(self.dir.as_str().unwrap());
            c
        }

        fn build_timeout(&self) -> Option<u64> { None }

        fn build_command(&self) -> Command {
            self.make_with_arg("build")
        }

        fn test_timeout(&self) -> Option<u64> { None }

        fn test_command(&self) -> Command {
            self.make_with_arg("test")
        }
    }
}

#[cfg(test)]
mod build_tests {
    use super::{TestSuccess, Pass, Fail, WholeBuildable};
    use super::testing::TestingRequest;

    fn req(name: &str) -> TestingRequest {
        TestingRequest::new(format!("test/{}", name),
                            "test/makefile".to_string())
    }

    #[test]
    fn makefile_copy_ok() {
        assert!(req("compile_error").setup_env().is_ok());
    }

    #[test]
    fn expected_compile_failure() {
        let r = req("compile_error");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_err());
    }

    #[test]
    fn expected_compile_success() {
        let r = req("compile_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
    }

    #[test]
    fn testing_parsing_empty_success() {
        let r = req("testing_parsing_empty_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
        let res = r.do_testing();
        assert!(res.is_ok());
        assert_eq!(res.unwrap().len(), 0);
    }

    #[test]
    fn testing_parsing_nonempty_success() {
        let r = req("testing_parsing_nonempty_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
        let res = r.do_testing();

        assert!(res.is_ok());
        let u = res.unwrap();
        assert_eq!(u.len(), 2);

        let t1 = u.find_equiv(&"test1".to_string());
        assert!(t1.is_some());
        assert_eq!(t1.unwrap(), &Pass);

        let t2 = u.find_equiv(&"test2".to_string());
        assert!(t2.is_some());
        assert_eq!(t2.unwrap(), &Fail);
    }

    #[test]
    fn test_whole_build() {
        match req("test_whole_build").whole_build() {
            TestSuccess(u) => {
                let t1 = u.find_equiv(&"test1".to_string());
                assert!(t1.is_some());
                assert_eq!(t1.unwrap(), &Pass);

                let t2 = u.find_equiv(&"test2".to_string());
                assert!(t2.is_some());
                assert_eq!(t2.unwrap(), &Fail);
            },
            _ => { assert!(false); }
        };
    }
}
<commit_msg>Code now compiles with the newest Rust (needed addition of AsRefReader).<commit_after>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::collections::HashMap;
use std::io::{BufferedReader, AsRefReader, RefReader, IoResult,
              IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    let mut p = try!(c.spawn());
    p.set_timeout(timeout);
    Ok(p)
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
pub fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    (try!(
        (try!(
            spawn_with_timeout(c, timeout)))
            .wait())).if_ok(on_success)
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

#[deriving(Show, PartialEq)]
pub enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command, timeout: Option<u64>) -> IoResult<ProcessReader> {
        spawn_with_timeout(cmd, timeout).map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            self.p.stdout.as_mut().unwrap().by_ref())
    }
}

fn parse_test_result(line: &str) -> IoResult<TestResult> {
    match line {
        "PASS" => Ok(Pass),
        "FAIL" => Ok(Fail),
        _ => Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test result",
                detail: Some(line.to_string())
            })
    }
}

fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
    let results: Vec<&str> = line.split_str(":").collect();
    if results.len() == 2 {
        parse_test_result(results[1]).map(|res| {
            (results[0].to_string(), res)
        })
    } else {
        Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test string",
                detail: Some(line.to_string())
            })
    }
}

#[deriving(Show)]
pub enum BuildResult {
    SetupEnvFailure(IoError),
    BuildFailure(IoError),
    TestFailure(IoError),
    TestSuccess(HashMap<String, TestResult>)
}

pub trait WholeBuildable {
    // BEGIN FUNCTIONS TO IMPLEMENT
    fn env_timeout(&self) -> Option<u64>;
    fn env_command(&self) -> Command;

    fn build_timeout(&self) -> Option<u64>;
    fn build_command(&self) -> Command;

    fn test_timeout(&self) -> Option<u64>;
    fn test_command(&self) -> Command;
    // END FUNCTIONS TO IMPLEMENT

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<()> {
        run_command(&self.env_command(), self.env_timeout(), ())
    }

    fn do_build(&self) -> IoResult<()> {
        run_command(&self.build_command(), self.build_timeout(), ())
    }

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        let mut reader =
            try!(ProcessReader::new(&self.test_command(), self.test_timeout()));
        let mut map = HashMap::new();
        for op_line in reader.output_reader().lines() {
            let (k, v) = try!(
                parse_line(
                    try!(op_line).as_slice().trim()));
            map.insert(k, v);
        }

        Ok(map)
    }

    fn whole_build(&self) -> BuildResult {
        // Because we have different results for different kinds
        // of failures, we cannot use `try!`
        match self.setup_env() {
            Ok(_) => {
                match self.do_build() {
                    Ok(_) => {
                        match self.do_testing() {
                            Ok(res) => TestSuccess(res),
                            Err(e) => TestFailure(e)
                        }
                    },
                    Err(e) => BuildFailure(e)
                }
            },
            Err(e) => SetupEnvFailure(e)
        }
    }
}

pub trait ToWholeBuildable<A : WholeBuildable> {
    fn to_whole_buildable(&self) -> A;
}

#[cfg(test)]
mod process_tests {
    use std::io::process::Command;
    use super::{run_command, ProcessReader};

    #[test]
    fn echo_ok() {
        assert!(run_command(&*Command::new("echo").arg("foobar"), None, ()).is_ok());
    }

    #[test]
    fn false_ok() {
        assert!(run_command(&Command::new("false"), None, ()).is_err());
    }

    fn output_from_command(cmd: &Command) -> Vec<String> {
        let pr = ProcessReader::new(cmd, None);
        assert!(pr.is_ok());
        pr.unwrap().output_reader().lines()
            .map(|line| {
                assert!(line.is_ok());
                line.unwrap().as_slice().trim().to_string()
            }).collect()
    }

    #[test]
    fn output_single_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foobar"));
        assert_eq!(lines.len(), 1);
        assert_eq!(lines[0].as_slice(), "foobar");
    }

    #[test]
    fn output_multi_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foo; echo bar"));
        assert_eq!(lines.len(), 2);
        assert_eq!(lines[0].as_slice(), "foo");
        assert_eq!(lines[1].as_slice(), "bar");
    }
}

#[cfg(test)]
mod parse_tests {
    use super::{parse_test_result, Pass, Fail, parse_line};

    #[test]
    fn parse_test_pass() {
        let res = parse_test_result("PASS");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Pass);
    }

    #[test]
    fn parse_test_fail() {
        let res = parse_test_result("FAIL");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Fail);
    }

    #[test]
    fn parse_test_bad_test() {
        let res = parse_test_result("foobar");
        assert!(res.is_err());
    }

    #[test]
    fn parse_valid_test_line() {
        let res = parse_line("my test:PASS");
        assert!(res.is_ok());
        let (key, result) = res.unwrap();
        assert_eq!(key.as_slice(), "my test");
        assert_eq!(result, Pass);
    }

    #[test]
    fn parse_invalid_test_line() {
        assert!(parse_line("this:is:PASS").is_err());
    }
}

pub mod testing {
    use std::io::process::Command;
    use std::path::BytesContainer;

    use super::{run_command, WholeBuildable};

    pub struct TestingRequest {
        dir: Path, // directory where the build is to be performed
        makefile_loc: Path // where the makefile is located
    }

    impl TestingRequest {
        pub fn new<A : BytesContainer>(dir: A, makefile_loc: A) -> TestingRequest {
            TestingRequest {
                dir: Path::new(dir),
                makefile_loc: Path::new(makefile_loc)
            }
        }

        fn make_with_arg<A : ToCStr>(&self, arg: A) -> Command {
            let mut c = Command::new("make");
            c.arg("-s").arg(arg).cwd(&self.dir);
            c
        }
    }

    impl Drop for TestingRequest {
        /// Automatically deletes the copied-over makefile on test end,
        /// along with any applicable executables (namely `a.out`)
        #[allow(unused_must_use)]
        fn drop(&mut self) {
            let dir = self.dir.as_str().unwrap();
            run_command(
                &*Command::new("rm")
                    .arg(format!("{}/makefile", dir))
                    .arg(format!("{}/a.out", dir)),
                None,
                ());
        }
    }

    impl WholeBuildable for TestingRequest {
        fn env_timeout(&self) -> Option<u64> { None }

        fn env_command(&self) -> Command {
            let mut c = Command::new("cp");
            c.arg(self.makefile_loc.as_str().unwrap());
            c.arg(self.dir.as_str().unwrap());
            c
        }

        fn build_timeout(&self) -> Option<u64> { None }

        fn build_command(&self) -> Command {
            self.make_with_arg("build")
        }

        fn test_timeout(&self) -> Option<u64> { None }

        fn test_command(&self) -> Command {
            self.make_with_arg("test")
        }
    }
}

#[cfg(test)]
mod build_tests {
    use super::{TestSuccess, Pass, Fail, WholeBuildable};
    use super::testing::TestingRequest;

    fn req(name: &str) -> TestingRequest {
        TestingRequest::new(format!("test/{}", name),
                            "test/makefile".to_string())
    }

    #[test]
    fn makefile_copy_ok() {
        assert!(req("compile_error").setup_env().is_ok());
    }

    #[test]
    fn expected_compile_failure() {
        let r = req("compile_error");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_err());
    }

    #[test]
    fn expected_compile_success() {
        let r = req("compile_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
    }

    #[test]
    fn testing_parsing_empty_success() {
        let r = req("testing_parsing_empty_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
        let res = r.do_testing();
        assert!(res.is_ok());
        assert_eq!(res.unwrap().len(), 0);
    }

    #[test]
    fn testing_parsing_nonempty_success() {
        let r = req("testing_parsing_nonempty_success");
        assert!(r.setup_env().is_ok());
        assert!(r.do_build().is_ok());
        let res = r.do_testing();

        assert!(res.is_ok());
        let u = res.unwrap();
        assert_eq!(u.len(), 2);

        let t1 = u.find_equiv(&"test1".to_string());
        assert!(t1.is_some());
        assert_eq!(t1.unwrap(), &Pass);

        let t2 = u.find_equiv(&"test2".to_string());
        assert!(t2.is_some());
        assert_eq!(t2.unwrap(), &Fail);
    }

    #[test]
    fn test_whole_build() {
        match req("test_whole_build").whole_build() {
            TestSuccess(u) => {
                let t1 = u.find_equiv(&"test1".to_string());
                assert!(t1.is_some());
                assert_eq!(t1.unwrap(), &Pass);

                let t2 = u.find_equiv(&"test2".to_string());
                assert!(t2.is_some());
                assert_eq!(t2.unwrap(), &Fail);
            },
            _ => { assert!(false); }
        };
    }
}
<|endoftext|>