<commit_before>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/org/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<commit_msg>Web auth<commit_after>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, ContentType, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.ip().to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(ContentType::form_url_encoded());
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/org/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.ip().to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}&scope=read:org",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOM_STRUCT_DERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        ).arg(
            Arg::with_name("DONT_USE_COW")
                .required(false)
                .long("dont_use_cow")
                .short("c")
                .help("Don't use Cow for String and Byte types"),
        ).get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOM_STRUCT_DERIVE").unwrap_or("")
        .split(",").map(|s| s.to_string()).collect();

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?.single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(!matches.is_present("HEADERS"))
    .dont_use_cow(matches.is_present("DONT_USE_COW"))
    .custom_struct_derive(custom_struct_derive);

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<commit_msg>cmd line now uses -D for not using cow<commit_after>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate failure;
extern crate log;
extern crate pb_rs;

use clap::{App, Arg};
use pb_rs::types::FileDescriptor;
use pb_rs::ConfigBuilder;
use std::path::{Path, PathBuf};

fn run() -> Result<(), ::failure::Error> {
    let matches = App::new(crate_name!())
        .about(crate_description!())
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .arg(
            Arg::with_name("OUTPUT")
                .required(false)
                .long("output")
                .short("o")
                .takes_value(true)
                .help("Generated file name, defaults to INPUT with 'rs' extension, cannot be used with --output_directory")
                .validator(|x| extension_matches(x, "rs")),
        ).arg(
            Arg::with_name("OUTPUT_DIR")
                .required(false)
                .long("output_directory")
                .short("d")
                .takes_value(true)
                .help("Output directory of generated code, cannot be used with --output"),
        ).arg(
            Arg::with_name("INCLUDE_PATH")
                .required(false)
                .long("include")
                .short("I")
                .takes_value(true)
                .help("Path to search for imported protobufs"),
        ).arg(
            Arg::with_name("SINGLE_MOD")
                .required(false)
                .long("single-mod")
                .short("s")
                .help("Omit generation of modules for each package when there is only one package"),
        ).arg(
            Arg::with_name("NO_OUTPUT")
                .required(false)
                .long("no-output")
                .short("n")
                .help(
                    "Show enums and messages in this .proto file, including those imported. \
                     No code generated",
                ),
        ).arg(
            Arg::with_name("INPUT")
                .multiple(true)
                .help("The .proto files used to generate quick-protobuf code")
                .validator(|x| extension_matches(x, "proto")),
        ).arg(
            Arg::with_name("CYCLE")
                .long("error-cycle")
                .short("e")
                .required(false)
                .help("Error out if recursive messages do not have optional fields"),
        ).arg(
            Arg::with_name("HEADERS")
                .long("no-headers")
                .short("H")
                .required(false)
                .help("Do not add module comments and module attributes in generated file"),
        ).arg(
            Arg::with_name("CUSTOM_STRUCT_DERIVE")
                .long("custom_struct_derive")
                .short("C")
                .required(false)
                .help("The comma separated values to add to #[derive(...)] for every struct"),
        ).arg(
            Arg::with_name("DONT_USE_COW")
                .required(false)
                .long("dont_use_cow")
                .short("D")
                .help("Don't use Cow for String and Byte types"),
        ).get_matches();

    let in_files = path_vec(values_t!(matches, "INPUT", String));
    let include_paths = path_vec(values_t!(matches, "INCLUDE_PATH", String));
    let out_file = matches.value_of("OUTPUT").map(|o| PathBuf::from(o));
    let out_dir = matches.value_of("OUTPUT_DIR").map(|o| PathBuf::from(o));
    let custom_struct_derive: Vec<String> = matches.value_of("CUSTOM_STRUCT_DERIVE").unwrap_or("")
        .split(",").map(|s| s.to_string()).collect();

    let compiler = ConfigBuilder::new(
        &in_files,
        out_file.as_ref(),
        out_dir.as_ref(),
        &include_paths,
    )?.single_module(matches.is_present("SINGLE_MOD"))
    .no_output(matches.is_present("NO_OUTPUT"))
    .error_cycle(matches.is_present("CYCLE"))
    .headers(!matches.is_present("HEADERS"))
    .dont_use_cow(matches.is_present("DONT_USE_COW"))
    .custom_struct_derive(custom_struct_derive);

    FileDescriptor::run(&compiler.build()).map_err(|e| e.into())
}

fn extension_matches<P: AsRef<Path>>(path: P, expected: &str) -> std::result::Result<(), String> {
    match path.as_ref().extension() {
        Some(x) if x == expected => Ok(()),
        Some(x) => Err(format!(
            "Expected path with extension '{}', not: '{}'",
            expected,
            x.to_string_lossy()
        )),
        None => Err(format!("Expected path with extension '{}'", expected)),
    }
}

fn path_vec(maybe_vec: std::result::Result<Vec<String>, clap::Error>) -> Vec<PathBuf> {
    maybe_vec
        .unwrap_or_else(|_| Vec::new())
        .iter()
        .map(|s| s.into())
        .collect()
}

fn main() {
    env_logger::init();
    ::std::process::exit({
        if let Err(e) = run() {
            eprintln!("pb-rs fatal error");
            for e in e.iter_chain() {
                eprintln!("  - {}", e);
            }
            1
        } else {
            0
        }
    });
}
<|endoftext|>


<commit_before>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl<'visit, 'cx, 'gcx, 'tcx> GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn remove_never_initialized_mut_locals(into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                 never_initialized_mut_locals={:?}",
                statement, local, self.never_initialized_mut_locals
            );
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<commit_msg>Elide lifetimes not used<commit_after>use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{
    Local, Location, Place, PlaceBase, Statement, StatementKind, TerminatorKind
};

use rustc_data_structures::fx::FxHashSet;

use crate::borrow_check::MirBorrowckCtxt;

impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
    /// Walks the MIR adding to the set of `used_mut` locals that will be ignored for the purposes
    /// of the `unused_mut` lint.
    ///
    /// `temporary_used_locals` should contain locals that were found to be temporary, mutable and
    ///  used from borrow checking. This function looks for assignments into these locals from
    ///  user-declared locals and adds those user-defined locals to the `used_mut` set. This can
    ///  occur due to a rare case involving upvars in closures.
    ///
    /// `never_initialized_mut_locals` should contain the set of user-declared mutable locals
    ///  (not arguments) that have not already been marked as being used.
    ///  This function then looks for assignments from statements or the terminator into the locals
    ///  from this set and removes them from the set. This leaves only those locals that have not
    ///  been assigned to - this set is used as a proxy for locals that were not initialized due to
    ///  unreachable code. These locals are then considered "used" to silence the lint for them.
    ///  See #55344 for context.
    crate fn gather_used_muts(
        &mut self,
        temporary_used_locals: FxHashSet<Local>,
        mut never_initialized_mut_locals: FxHashSet<Local>,
    ) {
        {
            let mut visitor = GatherUsedMutsVisitor {
                temporary_used_locals,
                never_initialized_mut_locals: &mut never_initialized_mut_locals,
                mbcx: self,
            };
            visitor.visit_body(visitor.mbcx.mir);
        }

        // Take the union of the existed `used_mut` set with those variables we've found were
        // never initialized.
        debug!("gather_used_muts: never_initialized_mut_locals={:?}", never_initialized_mut_locals);
        self.used_mut = self.used_mut.union(&never_initialized_mut_locals).cloned().collect();
    }
}

/// MIR visitor for collecting used mutable variables.
/// The 'visit lifetime represents the duration of the MIR walk.
struct GatherUsedMutsVisitor<'visit, 'cx: 'visit, 'gcx: 'tcx, 'tcx: 'cx> {
    temporary_used_locals: FxHashSet<Local>,
    never_initialized_mut_locals: &'visit mut FxHashSet<Local>,
    mbcx: &'visit mut MirBorrowckCtxt<'cx, 'gcx, 'tcx>,
}

impl GatherUsedMutsVisitor<'_, '_, '_, '_> {
    fn remove_never_initialized_mut_locals(into: &Place) {
        // Remove any locals that we found were initialized from the
        // `never_initialized_mut_locals` set. At the end, the only remaining locals will
        // be those that were never initialized - we will consider those as being used as
        // they will either have been removed by unreachable code optimizations; or linted
        // as unused variables.
        if let Some(local) = into.base_local() {
            debug!(
                "visit_statement: statement={:?} local={:?} \
                 never_initialized_mut_locals={:?}",
                statement, local, self.never_initialized_mut_locals
            );
            let _ = self.never_initialized_mut_locals.remove(&local);
        }
    }
}

impl<'visit, 'cx, 'gcx, 'tcx> Visitor<'tcx> for GatherUsedMutsVisitor<'visit, 'cx, 'gcx, 'tcx> {
    fn visit_terminator_kind(
        &mut self,
        kind: &TerminatorKind<'tcx>,
        _location: Location,
    ) {
        debug!("visit_terminator_kind: kind={:?}", kind);
        match &kind {
            TerminatorKind::Call { destination: Some((into, _)), .. } => {
                self.remove_never_initialized_mut_locals(&into);
            },
            TerminatorKind::DropAndReplace { location, .. } => {
                self.remove_never_initialized_mut_locals(&location);
            },
            _ => {},
        }
    }

    fn visit_statement(
        &mut self,
        statement: &Statement<'tcx>,
        _location: Location,
    ) {
        match &statement.kind {
            StatementKind::Assign(into, _) => {
                self.remove_never_initialized_mut_locals(into);
            },
            _ => {},
        }
    }

    fn visit_local(
        &mut self,
        local: &Local,
        place_context: PlaceContext,
        location: Location,
    ) {
        if place_context.is_place_assignment() && self.temporary_used_locals.contains(local) {
            // Propagate the Local assigned at this Location as a used mutable local variable
            for moi in &self.mbcx.move_data.loc_map[location] {
                let mpi = &self.mbcx.move_data.moves[*moi].path;
                let path = &self.mbcx.move_data.move_paths[*mpi];
                debug!(
                    "assignment of {:?} to {:?}, adding {:?} to used mutable set",
                    path.place, local, path.place
                );
                if let Place::Base(PlaceBase::Local(user_local)) = path.place {
                    self.mbcx.used_mut.insert(user_local);
                }
            }
        }
    }
}
<|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/. */

//! DOM bindings for `CharacterData`.

use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, NodeCast};
use dom::bindings::js::JSRef;
use dom::bindings::error::{Fallible, ErrorResult, IndexSize};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::node::{CommentNodeTypeId, Node, NodeTypeId, TextNodeTypeId, ProcessingInstructionNodeTypeId, NodeHelpers};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct CharacterData {
    pub node: Node,
    pub data: DOMString,
}

impl CharacterDataDerived for EventTarget {
    fn is_characterdata(&self) -> bool {
        match self.type_id {
            NodeTargetTypeId(TextNodeTypeId) |
            NodeTargetTypeId(CommentNodeTypeId) |
            NodeTargetTypeId(ProcessingInstructionNodeTypeId) => true,
            _ => false
        }
    }
}

impl CharacterData {
    pub fn new_inherited(id: NodeTypeId, data: DOMString, document: &JSRef<Document>) -> CharacterData {
        CharacterData {
            node: Node::new_inherited(id, document),
            data: data
        }
    }
}

pub trait CharacterDataMethods {
    fn Data(&self) -> DOMString;
    fn SetData(&mut self, arg: DOMString) -> ErrorResult;
    fn Length(&self) -> u32;
    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString>;
    fn AppendData(&mut self, arg: DOMString) -> ErrorResult;
    fn InsertData(&mut self, _offset: u32, _arg: DOMString) -> ErrorResult;
    fn DeleteData(&mut self, _offset: u32, _count: u32) -> ErrorResult;
    fn ReplaceData(&mut self, _offset: u32, _count: u32, _arg: DOMString) -> ErrorResult;
    fn Remove(&mut self);
}

impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> {
    fn Data(&self) -> DOMString {
        self.data.clone()
    }

    fn SetData(&mut self, arg: DOMString) -> ErrorResult {
        self.data = arg;
        Ok(())
    }

    fn Length(&self) -> u32 {
        self.data.len() as u32
    }

    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
        Ok(self.data.slice(offset as uint, count as uint).to_str())
    }

    fn AppendData(&mut self, arg: DOMString) -> ErrorResult {
        self.data = self.data + arg;
        Ok(())
    }

    fn InsertData(&mut self, offset: u32, arg: DOMString) -> ErrorResult {
        self.ReplaceData(offset, 0, arg)
    }

    fn DeleteData(&mut self, offset: u32, count: u32) -> ErrorResult {
        self.ReplaceData(offset, count, "".to_owned())
    }

    fn ReplaceData(&mut self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
        let length = self.data.len() as u32;
        if offset > length {
            return Err(IndexSize);
        }
        let count = if offset + count > length {
            length - offset
        } else {
            count
        };
        let mut data = self.data.slice(0, offset as uint).to_strbuf();
        data.push_str(arg);
        data.push_str(self.data.slice((offset + count) as uint, length as uint));
        self.data = data.into_owned();
        // FIXME: Once we have `Range`, we should implement step7 to step11
        Ok(())
    }

    // http://dom.spec.whatwg.org/#dom-childnode-remove
    fn Remove(&mut self) {
        let node: &mut JSRef<Node> = NodeCast::from_mut_ref(self);
        node.remove_self();
    }
}

impl Reflectable for CharacterData {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.node.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.node.mut_reflector()
    }
}
<commit_msg>Remove needless 'self' mutability from CharacterDataMethods::Remove().<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/. */

//! DOM bindings for `CharacterData`.

use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, NodeCast};
use dom::bindings::js::JSRef;
use dom::bindings::error::{Fallible, ErrorResult, IndexSize};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::node::{CommentNodeTypeId, Node, NodeTypeId, TextNodeTypeId, ProcessingInstructionNodeTypeId, NodeHelpers};
use servo_util::str::DOMString;

#[deriving(Encodable)]
pub struct CharacterData {
    pub node: Node,
    pub data: DOMString,
}

impl CharacterDataDerived for EventTarget {
    fn is_characterdata(&self) -> bool {
        match self.type_id {
            NodeTargetTypeId(TextNodeTypeId) |
            NodeTargetTypeId(CommentNodeTypeId) |
            NodeTargetTypeId(ProcessingInstructionNodeTypeId) => true,
            _ => false
        }
    }
}

impl CharacterData {
    pub fn new_inherited(id: NodeTypeId, data: DOMString, document: &JSRef<Document>) -> CharacterData {
        CharacterData {
            node: Node::new_inherited(id, document),
            data: data
        }
    }
}

pub trait CharacterDataMethods {
    fn Data(&self) -> DOMString;
    fn SetData(&mut self, arg: DOMString) -> ErrorResult;
    fn Length(&self) -> u32;
    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString>;
    fn AppendData(&mut self, arg: DOMString) -> ErrorResult;
    fn InsertData(&mut self, _offset: u32, _arg: DOMString) -> ErrorResult;
    fn DeleteData(&mut self, _offset: u32, _count: u32) -> ErrorResult;
    fn ReplaceData(&mut self, _offset: u32, _count: u32, _arg: DOMString) -> ErrorResult;
    fn Remove(&self);
}

impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> {
    fn Data(&self) -> DOMString {
        self.data.clone()
    }

    fn SetData(&mut self, arg: DOMString) -> ErrorResult {
        self.data = arg;
        Ok(())
    }

    fn Length(&self) -> u32 {
        self.data.len() as u32
    }

    fn SubstringData(&self, offset: u32, count: u32) -> Fallible<DOMString> {
        Ok(self.data.slice(offset as uint, count as uint).to_str())
    }

    fn AppendData(&mut self, arg: DOMString) -> ErrorResult {
        self.data = self.data + arg;
        Ok(())
    }

    fn InsertData(&mut self, offset: u32, arg: DOMString) -> ErrorResult {
        self.ReplaceData(offset, 0, arg)
    }

    fn DeleteData(&mut self, offset: u32, count: u32) -> ErrorResult {
        self.ReplaceData(offset, count, "".to_owned())
    }

    fn ReplaceData(&mut self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
        let length = self.data.len() as u32;
        if offset > length {
            return Err(IndexSize);
        }
        let count = if offset + count > length {
            length - offset
        } else {
            count
        };
        let mut data = self.data.slice(0, offset as uint).to_strbuf();
        data.push_str(arg);
        data.push_str(self.data.slice((offset + count) as uint, length as uint));
        self.data = data.into_owned();
        // FIXME: Once we have `Range`, we should implement step7 to step11
        Ok(())
    }

    // http://dom.spec.whatwg.org/#dom-childnode-remove
    fn Remove(&self) {
        let node: &JSRef<Node> = NodeCast::from_ref(self);
        node.remove_self();
    }
}

impl Reflectable for CharacterData {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.node.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.node.mut_reflector()
    }
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for struct constructors where all fields are shorthand and
    /// the order of the field init shorthand in the constructor is inconsistent
    /// with the order in the struct definition.
    ///
    /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
    /// resulted instance as the below example indicates,
    ///
    /// ```rust
    /// #[derive(Debug, PartialEq, Eq)]
    /// struct Foo {
    ///     x: i32,
    ///     y: i32,
    /// }
    /// let x = 1;
    /// let y = 2;
    ///
    /// // This assertion never fails:
    /// assert_eq!(Foo { x, y }, Foo { y, x });
    /// ```
    ///
    /// inconsistent order can be confusing and decreases readability and consistency.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct Foo {
    ///     x: i32,
    ///     y: i32,
    /// }
    /// let x = 1;
    /// let y = 2;
    ///
    /// Foo { y, x };
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// # struct Foo {
    /// #     x: i32,
    /// #     y: i32,
    /// # }
    /// # let x = 1;
    /// # let y = 2;
    /// Foo { x, y };
    /// ```
    pub INCONSISTENT_STRUCT_CONSTRUCTOR,
    style,
    "the order of the field init shorthand is inconsistent with the order in the struct definition"
}

declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);

impl LateLintPass<'_> for InconsistentStructConstructor {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
        if_chain! {
            if let ExprKind::Struct(qpath, fields, base) = expr.kind;
            let ty = cx.typeck_results().expr_ty(expr);
            if let Some(adt_def) = ty.ty_adt_def();
            if adt_def.is_struct();
            if let Some(variant) = adt_def.variants.iter().next();
            if fields.iter().all(|f| f.is_shorthand);
            then {
                let mut def_order_map = FxHashMap::default();
                for (idx, field) in variant.fields.iter().enumerate() {
                    def_order_map.insert(field.ident.name, idx);
                }

                if is_consistent_order(fields, &def_order_map) {
                    return;
                }

                let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
                ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);

                let mut fields_snippet = String::new();
                let (last_ident, idents) = ordered_fields.split_last().unwrap();
                for ident in idents {
                    fields_snippet.push_str(&format!("{}, ", ident));
                }
                fields_snippet.push_str(&last_ident.to_string());

                let base_snippet = if let Some(base) = base {
                        format!(", ..{}", snippet(cx, base.span, ".."))
                    } else {
                        String::new()
                    };

                let sugg = format!("{} {{ {}{} }}",
                    snippet(cx, qpath.span(), ".."),
                    fields_snippet,
                    base_snippet,
                    );

                span_lint_and_sugg(
                    cx,
                    INCONSISTENT_STRUCT_CONSTRUCTOR,
                    expr.span,
                    "struct constructor field order is inconsistent with struct definition field order",
                    "try",
                    sugg,
                    Applicability::MachineApplicable,
                )
            }
        }
    }
}

// Check whether the order of the fields in the constructor is consistent with the order in the
// definition.
fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool {
    let mut cur_idx = usize::MIN;
    for f in fields {
        let next_idx = def_order_map[&f.ident.name];
        if cur_idx > next_idx {
            return false;
        }
        cur_idx = next_idx;
    }

    true
}
<commit_msg>Fixed incosistent_struct_constructor triggers in macro-generated code<commit_after>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;

declare_clippy_lint! {
    /// **What it does:** Checks for struct constructors where all fields are shorthand and
    /// the order of the field init shorthand in the constructor is inconsistent
    /// with the order in the struct definition.
    ///
    /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
    /// resulted instance as the below example indicates,
    ///
    /// ```rust
    /// #[derive(Debug, PartialEq, Eq)]
    /// struct Foo {
    ///     x: i32,
    ///     y: i32,
    /// }
    /// let x = 1;
    /// let y = 2;
    ///
    /// // This assertion never fails:
    /// assert_eq!(Foo { x, y }, Foo { y, x });
    /// ```
    ///
    /// inconsistent order can be confusing and decreases readability and consistency.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```rust
    /// struct Foo {
    ///     x: i32,
    ///     y: i32,
    /// }
    /// let x = 1;
    /// let y = 2;
    ///
    /// Foo { y, x };
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// # struct Foo {
    /// #     x: i32,
    /// #     y: i32,
    /// # }
    /// # let x = 1;
    /// # let y = 2;
    /// Foo { x, y };
    /// ```
    pub INCONSISTENT_STRUCT_CONSTRUCTOR,
    style,
    "the order of the field init shorthand is inconsistent with the order in the struct definition"
}

declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);

impl LateLintPass<'_> for InconsistentStructConstructor {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
        if_chain! {
            if !expr.span.from_expansion();
            if let ExprKind::Struct(qpath, fields, base) = expr.kind;
            let ty = cx.typeck_results().expr_ty(expr);
            if let Some(adt_def) = ty.ty_adt_def();
            if adt_def.is_struct();
            if let Some(variant) = adt_def.variants.iter().next();
            if fields.iter().all(|f| f.is_shorthand);
            then {
                let mut def_order_map = FxHashMap::default();
                for (idx, field) in variant.fields.iter().enumerate() {
                    def_order_map.insert(field.ident.name, idx);
                }

                if is_consistent_order(fields, &def_order_map) {
                    return;
                }

                let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect();
                ordered_fields.sort_unstable_by_key(|id| def_order_map[id]);

                let mut fields_snippet = String::new();
                let (last_ident, idents) = ordered_fields.split_last().unwrap();
                for ident in idents {
                    fields_snippet.push_str(&format!("{}, ", ident));
                }
                fields_snippet.push_str(&last_ident.to_string());

                let base_snippet = if let Some(base) = base {
                        format!(", ..{}", snippet(cx, base.span, ".."))
                    } else {
                        String::new()
                    };

                let sugg = format!("{} {{ {}{} }}",
                    snippet(cx, qpath.span(), ".."),
                    fields_snippet,
                    base_snippet,
                    );

                span_lint_and_sugg(
                    cx,
                    INCONSISTENT_STRUCT_CONSTRUCTOR,
                    expr.span,
                    "struct constructor field order is inconsistent with struct definition field order",
                    "try",
                    sugg,
                    Applicability::MachineApplicable,
                )
            }
        }
    }
}

// Check whether the order of the fields in the constructor is consistent with the order in the
// definition.
fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map: &FxHashMap<Symbol, usize>) -> bool {
    let mut cur_idx = usize::MIN;
    for f in fields {
        let next_idx = def_order_map[&f.ident.name];
        if cur_idx > next_idx {
            return false;
        }
        cur_idx = next_idx;
    }

    true
}
<|endoftext|>


<commit_before>/*
 * Implemenation of a bitfield as a vec. Only
 * supports bytes (Vec<u8>) as the underlying
 * storage.
 *
 * A future implementation should be more efficient,
 * this is just to get the job done for now.
 */
extern crate ssz;

use std::cmp::max;

#[derive(Eq, Clone, Default, Debug)]
pub struct BooleanBitfield{
    len: usize,
    vec: Vec<u8>
}

impl BooleanBitfield {
    /// Create a new bitfield with a length of zero.
    pub fn new() -> Self {
        Self {
            len: 0,
            vec: vec![0]
        }
    }

    /// Create a new bitfield of a certain capacity
    pub fn with_capacity(capacity: usize) -> Self {
        let mut vec = Vec::with_capacity(capacity / 8 + 1);
        vec.push(0);
        Self {
            len: 0,
            vec
        }
    }

    /// Read the value of a bit.
    ///
    /// Will return `true` if the bit has been set to `true`
    /// without then being set to `False`.
    pub fn get_bit(&self, i: usize) -> bool {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        if byte(i) >= self.vec.len() {
            false
        } else {
             self.vec[byte(i)] & (1 << (bit(i) as u8)) != 0
        }
    }

    /// Set the value of a bit.
    ///
    /// If this bit is larger than the length of the underlying byte
    /// array it will be extended.
    pub fn set_bit(&mut self, i: usize, to: bool) {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        self.len = max(self.len, i + 1);

        if byte(i) >= self.vec.len() {
            self.vec.resize(byte(i) + 1, 0);
        }
        if to {
            self.vec[byte(i)] =
                self.vec[byte(i)] |  (1 << (bit(i) as u8))
        } else {
            self.vec[byte(i)] =
                self.vec[byte(i)] & !(1 << (bit(i) as u8))
        }
    }

    /// Return the "length" of this bitfield. Length is defined as
    /// the highest bit that has been set.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn len(&self) -> usize { self.len }

    /// True if no bits have ever been set. A bit that is set and then
    /// unset will still count to the length of the bitfield.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn is_empty(&self) -> bool { self.len == 0 }

    /// Iterate through the underlying vector and count the number of
    /// true bits.
    pub fn num_true_bits(&self) -> u64 {
        let mut count: u64 = 0;
        for byte in &self.vec {
            for bit in 0..8 {
                if byte & (1 << (bit as u8)) != 0 {
                    count += 1;
                }
            }
        }
        count
    }

    /// Iterate through the underlying vector and find the highest
    /// set bit. Useful for instantiating a new instance from
    /// some set of bytes.
    pub fn compute_length(bytes: &[u8]) -> usize {
        for byte in (0..bytes.len()).rev() {
            for bit in (0..8).rev() {
                if byte & (1 << (bit as u8)) != 0 {
                    return (byte * 8) + bit
                }
            }
        }
        0
    }

    /// Clone and return the underlying byte array (`Vec<u8>`).
    pub fn to_be_vec(&self) -> Vec<u8> {
        let mut o = self.vec.clone();
        o.reverse();
        o
    }
}

impl<'a> From<&'a [u8]> for BooleanBitfield {
    fn from(input: &[u8]) -> Self {
        let mut vec = input.to_vec();
        vec.reverse();
        BooleanBitfield {
            vec,
            len: BooleanBitfield::compute_length(input)
        }
    }
}

impl PartialEq for BooleanBitfield {
    fn eq(&self, other: &BooleanBitfield) -> bool {
        (self.vec == other.vec) &
            (self.len == other.len)
    }
}

impl ssz::Encodable for BooleanBitfield {
    fn ssz_append(&self, s: &mut ssz::SszStream) {
        s.append_vec(&self.to_be_vec());
    }
}

impl ssz::Decodable for BooleanBitfield {
    fn ssz_decode(bytes: &[u8], index: usize)
        -> Result<(Self, usize), ssz::DecodeError>
    {
        let len = ssz::decode::decode_length(
            bytes,
            index,
            ssz::LENGTH_BYTES)?;
        if (ssz::LENGTH_BYTES + len) > bytes.len() {
            return Err(ssz::DecodeError::TooShort);
        }
        if len == 0 {
            Ok((BooleanBitfield::new(),
                index + ssz::LENGTH_BYTES))
        } else {
            let b = BooleanBitfield::
                from(&bytes[(index + 4)..(index + len + 4)]);
            let index = index + ssz::LENGTH_BYTES + len;
            Ok((b, index))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ssz::Decodable;

    #[test]
    fn test_ssz_encoding() {
        let mut b = BooleanBitfield::new();
        b.set_bit(8, true);

        let mut stream = ssz::SszStream::new();
        stream.append(&b);

        assert_eq!(stream.drain(), vec![0, 0, 0, 2, 1, 0]);
    }

    #[test]
    fn test_ssz_decoding() {
        /*
         * Correct input
         */
        let input = vec![0, 0, 0, 2, 1, 0];
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too long
         */
        let mut input = vec![0, 0, 0, 2, 1, 0];
        input.push(42);
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too short
         */
        let input = vec![0, 0, 0, 2, 1];
        let res = BooleanBitfield::ssz_decode(&input, 0);
        assert_eq!(res, Err(ssz::DecodeError::TooShort));
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_bitfield_set() {
        let mut b = BooleanBitfield::new();
        b.set_bit(0, false);
        assert_eq!(b.to_be_vec(), [0]);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        assert_eq!(b.to_be_vec(), [128]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [0]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        b.set_bit(0, true);
        assert_eq!(b.to_be_vec(), [129]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [1]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 9);
        b.set_bit(8, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 9);

        b = BooleanBitfield::new();
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [128, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 16);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [129, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 16);
    }

    #[test]
    fn test_bitfield_get() {
        let test_nums = vec![0, 8, 15, 42, 1337];
        for i in test_nums {
            let mut b = BooleanBitfield::new();
            assert_eq!(b.get_bit(i), false);
            b.set_bit(i, true);
            assert_eq!(b.get_bit(i), true);
            b.set_bit(i, true);
        }
    }

    #[test]
    fn test_bitfield_num_true_bits() {
        let mut b = BooleanBitfield::new();
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(15, true);
        assert_eq!(b.num_true_bits(), 1);
        b.set_bit(15, false);
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(0, true);
        b.set_bit(7, true);
        b.set_bit(8, true);
        b.set_bit(1337, true);
        assert_eq!(b.num_true_bits(), 4);
    }
}
<commit_msg>Add `num_bytes()` to bitfield<commit_after>/*
 * Implemenation of a bitfield as a vec. Only
 * supports bytes (Vec<u8>) as the underlying
 * storage.
 *
 * A future implementation should be more efficient,
 * this is just to get the job done for now.
 */
extern crate ssz;

use std::cmp::max;

#[derive(Eq, Clone, Default, Debug)]
pub struct BooleanBitfield{
    len: usize,
    vec: Vec<u8>
}

impl BooleanBitfield {
    /// Create a new bitfield with a length of zero.
    pub fn new() -> Self {
        Self {
            len: 0,
            vec: vec![0]
        }
    }

    /// Create a new bitfield of a certain capacity
    pub fn with_capacity(capacity: usize) -> Self {
        let mut vec = Vec::with_capacity(capacity / 8 + 1);
        vec.push(0);
        Self {
            len: 0,
            vec
        }
    }

    /// Read the value of a bit.
    ///
    /// Will return `true` if the bit has been set to `true`
    /// without then being set to `False`.
    pub fn get_bit(&self, i: usize) -> bool {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        if byte(i) >= self.vec.len() {
            false
        } else {
             self.vec[byte(i)] & (1 << (bit(i) as u8)) != 0
        }
    }

    /// Set the value of a bit.
    ///
    /// If this bit is larger than the length of the underlying byte
    /// array it will be extended.
    pub fn set_bit(&mut self, i: usize, to: bool) {
        let bit = |i: usize| i % 8;
        let byte = |i: usize| i / 8;

        self.len = max(self.len, i + 1);

        if byte(i) >= self.vec.len() {
            self.vec.resize(byte(i) + 1, 0);
        }
        if to {
            self.vec[byte(i)] =
                self.vec[byte(i)] |  (1 << (bit(i) as u8))
        } else {
            self.vec[byte(i)] =
                self.vec[byte(i)] & !(1 << (bit(i) as u8))
        }
    }

    /// Return the "length" of this bitfield. Length is defined as
    /// the highest bit that has been set.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn len(&self) -> usize { self.len }

    /// True if no bits have ever been set. A bit that is set and then
    /// unset will still count to the length of the bitfield.
    ///
    /// Note: this is distinct from the length of the underlying
    /// vector.
    pub fn is_empty(&self) -> bool { self.len == 0 }

    /// The number of bytes required to represent the bitfield.
    pub fn num_bytes(&self) -> usize { self.vec.len() }

    /// Iterate through the underlying vector and count the number of
    /// true bits.
    pub fn num_true_bits(&self) -> u64 {
        let mut count: u64 = 0;
        for byte in &self.vec {
            for bit in 0..8 {
                if byte & (1 << (bit as u8)) != 0 {
                    count += 1;
                }
            }
        }
        count
    }

    /// Iterate through the underlying vector and find the highest
    /// set bit. Useful for instantiating a new instance from
    /// some set of bytes.
    pub fn compute_length(bytes: &[u8]) -> usize {
        for byte in (0..bytes.len()).rev() {
            for bit in (0..8).rev() {
                if byte & (1 << (bit as u8)) != 0 {
                    return (byte * 8) + bit
                }
            }
        }
        0
    }

    /// Clone and return the underlying byte array (`Vec<u8>`).
    pub fn to_be_vec(&self) -> Vec<u8> {
        let mut o = self.vec.clone();
        o.reverse();
        o
    }
}

impl<'a> From<&'a [u8]> for BooleanBitfield {
    fn from(input: &[u8]) -> Self {
        let mut vec = input.to_vec();
        vec.reverse();
        BooleanBitfield {
            vec,
            len: BooleanBitfield::compute_length(input)
        }
    }
}

impl PartialEq for BooleanBitfield {
    fn eq(&self, other: &BooleanBitfield) -> bool {
        (self.vec == other.vec) &
            (self.len == other.len)
    }
}

impl ssz::Encodable for BooleanBitfield {
    fn ssz_append(&self, s: &mut ssz::SszStream) {
        s.append_vec(&self.to_be_vec());
    }
}

impl ssz::Decodable for BooleanBitfield {
    fn ssz_decode(bytes: &[u8], index: usize)
        -> Result<(Self, usize), ssz::DecodeError>
    {
        let len = ssz::decode::decode_length(
            bytes,
            index,
            ssz::LENGTH_BYTES)?;
        if (ssz::LENGTH_BYTES + len) > bytes.len() {
            return Err(ssz::DecodeError::TooShort);
        }
        if len == 0 {
            Ok((BooleanBitfield::new(),
                index + ssz::LENGTH_BYTES))
        } else {
            let b = BooleanBitfield::
                from(&bytes[(index + 4)..(index + len + 4)]);
            let index = index + ssz::LENGTH_BYTES + len;
            Ok((b, index))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use ssz::Decodable;

    #[test]
    fn test_ssz_encoding() {
        let mut b = BooleanBitfield::new();
        b.set_bit(8, true);

        let mut stream = ssz::SszStream::new();
        stream.append(&b);

        assert_eq!(stream.drain(), vec![0, 0, 0, 2, 1, 0]);
    }

    #[test]
    fn test_ssz_decoding() {
        /*
         * Correct input
         */
        let input = vec![0, 0, 0, 2, 1, 0];
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too long
         */
        let mut input = vec![0, 0, 0, 2, 1, 0];
        input.push(42);
        let (b, i) = BooleanBitfield::ssz_decode(&input, 0).unwrap();
        assert_eq!(i, 6);
        assert_eq!(b.num_true_bits(), 1);
        assert_eq!(b.get_bit(8), true);

        /*
         * Input too short
         */
        let input = vec![0, 0, 0, 2, 1];
        let res = BooleanBitfield::ssz_decode(&input, 0);
        assert_eq!(res, Err(ssz::DecodeError::TooShort));
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_new_bitfield_len() {
        let b = BooleanBitfield::new();
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);

        let b = BooleanBitfield::with_capacity(100);
        assert_eq!(b.len(), 0);
        assert_eq!(b.to_be_vec(), vec![0]);
    }

    #[test]
    fn test_bitfield_set() {
        let mut b = BooleanBitfield::new();
        b.set_bit(0, false);
        assert_eq!(b.to_be_vec(), [0]);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        assert_eq!(b.to_be_vec(), [128]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [0]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(7, true);
        b.set_bit(0, true);
        assert_eq!(b.to_be_vec(), [129]);
        b.set_bit(7, false);
        assert_eq!(b.to_be_vec(), [1]);
        assert_eq!(b.len(), 8);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 9);
        b.set_bit(8, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 9);

        b = BooleanBitfield::new();
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [128, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [0, 0]);
        assert_eq!(b.len(), 16);

        b = BooleanBitfield::new();
        b.set_bit(8, true);
        b.set_bit(15, true);
        assert_eq!(b.to_be_vec(), [129, 0]);
        b.set_bit(15, false);
        assert_eq!(b.to_be_vec(), [1, 0]);
        assert_eq!(b.len(), 16);
    }

    #[test]
    fn test_bitfield_get() {
        let test_nums = vec![0, 8, 15, 42, 1337];
        for i in test_nums {
            let mut b = BooleanBitfield::new();
            assert_eq!(b.get_bit(i), false);
            b.set_bit(i, true);
            assert_eq!(b.get_bit(i), true);
            b.set_bit(i, true);
        }
    }

    #[test]
    fn test_bitfield_num_true_bits() {
        let mut b = BooleanBitfield::new();
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(15, true);
        assert_eq!(b.num_true_bits(), 1);
        b.set_bit(15, false);
        assert_eq!(b.num_true_bits(), 0);
        b.set_bit(0, true);
        b.set_bit(7, true);
        b.set_bit(8, true);
        b.set_bit(1337, true);
        assert_eq!(b.num_true_bits(), 4);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::ShareContext;
use context::GraphicsContextMethods;

use extra::arc::Arc;
use geom::size::Size2D;
use io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use opengles::cgl::{CGLSetCurrentContext, CGLTexImageIOSurface2D, kCGLNoError, kCGLPFACompliant};
use opengles::cgl::{kCGLPFADoubleBuffer};
use opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};
use opengles::gl2;
use std::cast::transmute;
use std::cast;
use std::ptr::{null, to_unsafe_ptr};

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

/// Mac-specific interface to 3D graphics contexts.
pub struct GraphicsContext {
    cgl_context: Arc<CGLContextObj>,
}

impl GraphicsContext {
    /// Returns a new context, possibly shared with another context.
    #[fixed_stack_segment]
    fn new_possibly_shared(share_context: Option<GraphicsContext>) -> GraphicsContext {
        unsafe {
            // Choose a pixel format.
            let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
            let pixel_format = null();
            let pixel_format_count = 1;
            let gl_error = CGLChoosePixelFormat(transmute(&attributes[0]),
                                                to_unsafe_ptr(&pixel_format),
                                                to_unsafe_ptr(&pixel_format_count));
            assert!(gl_error == kCGLNoError);

            // Create the context.
            let cgl_context = null();
            let gl_error = match share_context {
                None => CGLCreateContext(pixel_format, null(), &cgl_context),
                Some(ref share_context) => {
                    let native = share_context.native();
                    CGLCreateContext(pixel_format, *native.get(), &cgl_context)
                }
            };
            assert!(gl_error == kCGLNoError);

            GraphicsContextMethods::wrap(Arc::new(cgl_context))
        }
    }
}

impl GraphicsContextMethods<CGLContextObj> for GraphicsContext {
    /// Wraps the given instance of the native Core OpenGL graphics context.
    fn wrap(instance: Arc<CGLContextObj>) -> GraphicsContext {
        GraphicsContext {
            cgl_context: instance
        }
    }

    /// Returns the underlying native 3D context without modifying its reference count.
    fn native(&self) -> Arc<CGLContextObj> {
        self.cgl_context.clone()
    }

    /// Creates a new offscreen 3D graphics context.
    fn new() -> GraphicsContext {
        GraphicsContext::new_possibly_shared(None)
    }

    /// Creates a new offscreen 3D graphics context shared with the given context.
    fn new_shared(share_context: GraphicsContext) -> GraphicsContext {
        GraphicsContext::new_possibly_shared(Some(share_context))
    }

    /// Makes this context the current context.
    #[fixed_stack_segment]
    fn make_current(&self) {
        unsafe {
            let gl_error = CGLSetCurrentContext(*self.cgl_context.get());
            assert!(gl_error == kCGLNoError)
        }
    }
}

impl Clone for GraphicsContext {
    fn clone(&self) -> GraphicsContext {
        GraphicsContextMethods::wrap(self.native())
    }
}

pub struct Context {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> GraphicsContext {
    let context: GraphicsContext = GraphicsContextMethods::new();
    context.make_current();
    context
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use core_foundation::boolean::CFBoolean;
    use core_foundation::number::CFNumber;
    use core_foundation::string::CFString;
    use core_foundation;
    use io_surface;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width = CFString::wrap_shared(kIOSurfaceWidth);
    let v_width = CFNumber::new(size.width as i32);

    let k_height = CFString::wrap_shared(kIOSurfaceHeight);
    let v_height = CFNumber::new(size.height as i32);

    let k_bytes_per_row = CFString::wrap_shared(kIOSurfaceBytesPerRow);
    let v_bytes_per_row = CFNumber::new(size.width as i32 * 4);

    let k_bytes_per_elem = CFString::wrap_shared(kIOSurfaceBytesPerElement);
    let v_bytes_per_elem = CFNumber::new(4i32);

    let k_is_global = CFString::wrap_shared(kIOSurfaceIsGlobal);
    let v_is_global = CFBoolean::true_value();

    io_surface::new(&core_foundation::dictionary::CFDictionary::new([
        (*k_width.contents.borrow_ref(),          *v_width.contents.borrow_type_ref()),
        (*k_height.contents.borrow_ref(),         *v_height.contents.borrow_type_ref()),
        (*k_bytes_per_row.contents.borrow_ref(),  *v_bytes_per_row.contents.borrow_type_ref()),
        (*k_bytes_per_elem.contents.borrow_ref(), *v_bytes_per_elem.contents.borrow_type_ref()),
        (*k_is_global.contents.borrow_ref(),      *v_is_global.contents.borrow_type_ref()),
    ]))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
#[fixed_stack_segment]
pub fn bind_surface_to_texture(context: &GraphicsContext, surface: &IOSurface, size: Size2D<int>) {
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let native = context.native();
        let gl_error = CGLTexImageIOSurface2D(*native.get(),
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              cast::transmute_copy(&surface.contents),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for Context {
    fn new(size: Size2D<int>) -> Context {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(size.clone());

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        Context {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<commit_msg>Update for new Core Foundation bindings.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::ShareContext;
use context::GraphicsContextMethods;

use extra::arc::Arc;
use geom::size::Size2D;
use io_surface::{IOSurface, kIOSurfaceBytesPerElement, kIOSurfaceBytesPerRow};
use io_surface::{kIOSurfaceHeight, kIOSurfaceIsGlobal, kIOSurfaceWidth};
use opengles::cgl::{CGLChoosePixelFormat, CGLContextObj, CGLCreateContext};
use opengles::cgl::{CGLSetCurrentContext, CGLTexImageIOSurface2D, kCGLNoError, kCGLPFACompliant};
use opengles::cgl::{kCGLPFADoubleBuffer};
use opengles::gl2::{BGRA, CLAMP_TO_EDGE, COLOR_ATTACHMENT0, FRAMEBUFFER};
use opengles::gl2::{FRAMEBUFFER_COMPLETE, GLenum, GLint, GLsizei, GLuint, LINEAR};
use opengles::gl2::{NEAREST, RGBA, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER};
use opengles::gl2::{TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, TEXTURE_WRAP_T};
use opengles::gl2::{UNSIGNED_INT_8_8_8_8_REV};
use opengles::gl2;

use std::cast;
use std::ptr;

// FIXME: This is not good.
#[link_args="-framework IOSurface -framework CoreFoundation"]
#[nolink]
extern {}

/// Mac-specific interface to 3D graphics contexts.
pub struct GraphicsContext {
    cgl_context: Arc<CGLContextObj>,
}

impl GraphicsContext {
    /// Returns a new context, possibly shared with another context.
    #[fixed_stack_segment]
    fn new_possibly_shared(share_context: Option<GraphicsContext>) -> GraphicsContext {
        unsafe {
            // Choose a pixel format.
            let attributes = [ kCGLPFADoubleBuffer, kCGLPFACompliant, 0 ];
            let mut pixel_format = ptr::null();
            let mut pixel_format_count = 1;
            let gl_error = CGLChoosePixelFormat(cast::transmute(&attributes[0]),
                                                &mut pixel_format,
                                                &mut pixel_format_count);
            assert!(gl_error == kCGLNoError);

            // Create the context.
            let cgl_context = ptr::null();
            let gl_error = match share_context {
                None => CGLCreateContext(pixel_format, ptr::null(), &cgl_context),
                Some(ref share_context) => {
                    let native = share_context.native();
                    CGLCreateContext(pixel_format, *native.get(), &cgl_context)
                }
            };
            assert!(gl_error == kCGLNoError);

            GraphicsContextMethods::wrap(Arc::new(cgl_context))
        }
    }
}

impl GraphicsContextMethods<CGLContextObj> for GraphicsContext {
    /// Wraps the given instance of the native Core OpenGL graphics context.
    fn wrap(instance: Arc<CGLContextObj>) -> GraphicsContext {
        GraphicsContext {
            cgl_context: instance
        }
    }

    /// Returns the underlying native 3D context without modifying its reference count.
    fn native(&self) -> Arc<CGLContextObj> {
        self.cgl_context.clone()
    }

    /// Creates a new offscreen 3D graphics context.
    fn new() -> GraphicsContext {
        GraphicsContext::new_possibly_shared(None)
    }

    /// Creates a new offscreen 3D graphics context shared with the given context.
    fn new_shared(share_context: GraphicsContext) -> GraphicsContext {
        GraphicsContext::new_possibly_shared(Some(share_context))
    }

    /// Makes this context the current context.
    #[fixed_stack_segment]
    fn make_current(&self) {
        unsafe {
            let gl_error = CGLSetCurrentContext(*self.cgl_context.get());
            assert!(gl_error == kCGLNoError)
        }
    }
}

impl Clone for GraphicsContext {
    fn clone(&self) -> GraphicsContext {
        GraphicsContextMethods::wrap(self.native())
    }
}

pub struct Context {
    surface: IOSurface,
    framebuffer: GLuint,
    texture: GLuint

    // FIXME: Needs drop.
}

pub fn init_cgl() -> GraphicsContext {
    let context: GraphicsContext = GraphicsContextMethods::new();
    context.make_current();
    context
}

pub fn init_surface(size: Size2D<int>) -> IOSurface {
    use core_foundation::boolean::CFBoolean;
    use core_foundation::string::CFString;
    use core_foundation::number::CFNumber;
    use core_foundation::dictionary::CFDictionary;
    use core_foundation::base::{CFType, TCFType};
    use io_surface;

    // TODO: dictionary constructor should be less ridiculous.
    // Or, we could add bindings for mutable dictionaries.
    let k_width: CFString = unsafe { TCFType::wrap_under_get_rule(kIOSurfaceWidth) };
    let v_width: CFNumber = FromPrimitive::from_i32(size.width as i32).unwrap();

    let k_height: CFString = unsafe { TCFType::wrap_under_get_rule(kIOSurfaceHeight) };
    let v_height: CFNumber = FromPrimitive::from_i32(size.height as i32).unwrap();

    let k_bytes_per_row: CFString = unsafe { TCFType::wrap_under_get_rule(kIOSurfaceBytesPerRow) };
    let v_bytes_per_row: CFNumber = FromPrimitive::from_i32(size.width as i32 * 4).unwrap();

    let k_bytes_per_elem: CFString = unsafe { TCFType::wrap_under_get_rule(kIOSurfaceBytesPerElement) };
    let v_bytes_per_elem: CFNumber = FromPrimitive::from_i32(4i32).unwrap();

    let k_is_global: CFString = unsafe { TCFType::wrap_under_get_rule(kIOSurfaceIsGlobal) };
    let v_is_global = CFBoolean::true_value();

    let pairs: ~[(CFType, CFType)] = ~[
        (k_width.as_CFType(), v_width.as_CFType()),
        (k_height.as_CFType(), v_height.as_CFType()),
        (k_bytes_per_row.as_CFType(), v_bytes_per_row.as_CFType()),
        (k_bytes_per_elem.as_CFType(), v_bytes_per_elem.as_CFType()),
        (k_is_global.as_CFType(), v_is_global.as_CFType()),
    ];

    io_surface::new(&CFDictionary::from_CFType_pairs(pairs))
}

pub fn init_texture() -> GLuint {
    gl2::enable(TEXTURE_RECTANGLE_ARB);

    let texture = gl2::gen_textures(1)[0];
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, texture);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_S, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_WRAP_T, CLAMP_TO_EDGE as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MAG_FILTER, LINEAR as GLint);
    gl2::tex_parameter_i(TEXTURE_RECTANGLE_ARB, TEXTURE_MIN_FILTER, NEAREST as GLint);
    return texture;
}

// Assumes the texture is already bound via gl2::bind_texture().
#[fixed_stack_segment]
pub fn bind_surface_to_texture(context: &GraphicsContext, surface: &IOSurface, size: Size2D<int>) {
    use core_foundation::base::TCFType;
    // FIXME: There should be safe wrappers for this.
    unsafe {
        let native = context.native();
        let gl_error = CGLTexImageIOSurface2D(*native.get(),
                                              TEXTURE_RECTANGLE_ARB,
                                              RGBA as GLenum,
                                              size.width as GLsizei,
                                              size.height as GLsizei,
                                              BGRA as GLenum,
                                              UNSIGNED_INT_8_8_8_8_REV,
                                              cast::transmute(surface.as_concrete_TypeRef()),
                                              0);
        assert!(gl_error == kCGLNoError);
    }
}

pub fn bind_texture_to_framebuffer(texture: GLuint) {
    gl2::bind_texture(TEXTURE_RECTANGLE_ARB, 0);
    gl2::framebuffer_texture_2d(FRAMEBUFFER, COLOR_ATTACHMENT0, TEXTURE_RECTANGLE_ARB, texture, 0);
    assert!(gl2::check_framebuffer_status(FRAMEBUFFER) == FRAMEBUFFER_COMPLETE);
}

impl ShareContext for Context {
    fn new(size: Size2D<int>) -> Context {
        // Initialize CGL.
        let context = init_cgl();

        // Create the surface.
        let surface = init_surface(size.clone());

        // Create a framebuffer.
        let framebuffer = gl2::gen_framebuffers(1)[0];
        gl2::bind_framebuffer(FRAMEBUFFER, framebuffer);

        // Create and bind to the texture.
        let texture = init_texture();
        bind_surface_to_texture(&context, &surface, size);

        // Bind the texture to the framebuffer.
        bind_texture_to_framebuffer(texture);

        Context {
            surface: surface,
            framebuffer: framebuffer,
            texture: texture
        }
    }

    fn flush(&self) {
        gl2::finish();
    }

    fn id(&self) -> int {
        self.surface.get_id() as int
    }
}

<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use target::TargetOptions;
use std::default::Default;

pub fn opts() -> TargetOptions {
    TargetOptions {
        linker: "cc".to_string(),
        dynamic_linking: true,
        executables: true,
        linker_is_gnu: true,
        has_rpath: true,
        pre_link_args: vec!(
            "-L/usr/local/lib".to_string(),
            "-L/usr/lib/gcc47".to_string(),
            // GNU-style linkers will use this to omit linking to libraries
            // which don't actually fulfill any relocations, but only for
            // libraries which follow this flag.  Thus, use it before
            // specifying libraries to link to.
            "-Wl,--as-needed".to_string(),
        ),
        position_independent_executables: true,
        archive_format: "bsd".to_string(),
        exe_allocation_crate: super::best_allocator(),
        .. Default::default()
    }
}
<commit_msg>Auto merge of #28107 - mneumann:dragonfly_ar, r=alexcrichton<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use target::TargetOptions;
use std::default::Default;

pub fn opts() -> TargetOptions {
    TargetOptions {
        linker: "cc".to_string(),
        dynamic_linking: true,
        executables: true,
        linker_is_gnu: true,
        has_rpath: true,
        pre_link_args: vec!(
            "-L/usr/local/lib".to_string(),
            "-L/usr/lib/gcc47".to_string(),
            // GNU-style linkers will use this to omit linking to libraries
            // which don't actually fulfill any relocations, but only for
            // libraries which follow this flag.  Thus, use it before
            // specifying libraries to link to.
            "-Wl,--as-needed".to_string(),
        ),
        position_independent_executables: true,
        archive_format: "gnu".to_string(),
        exe_allocation_crate: super::best_allocator(),
        .. Default::default()
    }
}
<|endoftext|>