<commit_before>// Copyright 2014-2018 The Rust 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 std::env;

#[macro_export]
macro_rules! get_version_info {
    () => {{
        let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
        let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
        let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
        let crate_name = String::from(env!("CARGO_PKG_NAME"));

        let host_compiler = $crate::get_channel();
        let commit_hash = option_env!("GIT_HASH").map(|s| s.to_string());
        let commit_date = option_env!("COMMIT_DATE").map(|s| s.to_string());

        VersionInfo {
            major,
            minor,
            patch,
            host_compiler,
            commit_hash,
            commit_date,
            crate_name,
        }
    }};
}

// some code taken and adapted from RLS and cargo
pub struct VersionInfo {
    pub major: u8,
    pub minor: u8,
    pub patch: u16,
    pub host_compiler: Option<String>,
    pub commit_hash: Option<String>,
    pub commit_date: Option<String>,
    pub crate_name: String,
}

impl std::fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.commit_hash.is_some() {
            write!(
                f,
                "{} {}.{}.{} ({} {})",
                self.crate_name,
                self.major,
                self.minor,
                self.patch,
                self.commit_hash.clone().unwrap_or_default().trim(),
                self.commit_date.clone().unwrap_or_default().trim(),
            )?;
        } else {
            write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
        }

        Ok(())
    }
}

impl std::fmt::Debug for VersionInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
            self.crate_name, self.major, self.minor, self.patch,
        )?;
        if self.commit_hash.is_some() {
            write!(
                f,
                ", commit_hash: \"{}\", commit_date: \"{}\" }}",
                self.commit_hash.clone().unwrap_or_default().trim(),
                self.commit_date.clone().unwrap_or_default().trim()
            )?;
        } else {
            write!(f, " }}")?;
        }

        Ok(())
    }
}

pub fn get_channel() -> Option<String> {
    if let Ok(channel) = env::var("CFG_RELEASE_CHANNEL") {
        Some(channel)
    } else {
        // we could ask ${RUSTC} -Vv and do some parsing and find out
        Some(String::from("nightly"))
    }
}

pub fn get_commit_hash() -> Option<String> {
    std::process::Command::new("git")
        .args(&["rev-parse", "--short", "HEAD"])
        .output()
        .ok()
        .and_then(|r| String::from_utf8(r.stdout).ok())
}

pub fn get_commit_date() -> Option<String> {
    std::process::Command::new("git")
        .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
        .output()
        .ok()
        .and_then(|r| String::from_utf8(r.stdout).ok())
}

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

    #[test]
    fn test_struct_local() {
        let vi = get_version_info!();
        assert_eq!(vi.major, 0);
        assert_eq!(vi.minor, 1);
        assert_eq!(vi.patch, 0);
        assert_eq!(vi.crate_name, "rustc_tools_util");
        // hard to make positive tests for these since they will always change
        assert!(vi.commit_hash.is_none());
        assert!(vi.commit_date.is_none());
    }

    #[test]
    fn test_display_local() {
        let vi = get_version_info!();
        assert_eq!(vi.to_string(), "rustc_tools_util 0.1.0");
    }

    #[test]
    fn test_debug_local() {
        let vi = get_version_info!();
        let s = format!("{:?}", vi);
        assert_eq!(
            s,
            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 1, patch: 0 }"
        );
    }

}
<commit_msg>Run rustfmt on rustc_tools_util<commit_after>// Copyright 2014-2018 The Rust 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 std::env;

#[macro_export]
macro_rules! get_version_info {
    () => {{
        let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
        let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
        let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
        let crate_name = String::from(env!("CARGO_PKG_NAME"));

        let host_compiler = $crate::get_channel();
        let commit_hash = option_env!("GIT_HASH").map(|s| s.to_string());
        let commit_date = option_env!("COMMIT_DATE").map(|s| s.to_string());

        VersionInfo {
            major,
            minor,
            patch,
            host_compiler,
            commit_hash,
            commit_date,
            crate_name,
        }
    }};
}

// some code taken and adapted from RLS and cargo
pub struct VersionInfo {
    pub major: u8,
    pub minor: u8,
    pub patch: u16,
    pub host_compiler: Option<String>,
    pub commit_hash: Option<String>,
    pub commit_date: Option<String>,
    pub crate_name: String,
}

impl std::fmt::Display for VersionInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.commit_hash.is_some() {
            write!(
                f,
                "{} {}.{}.{} ({} {})",
                self.crate_name,
                self.major,
                self.minor,
                self.patch,
                self.commit_hash.clone().unwrap_or_default().trim(),
                self.commit_date.clone().unwrap_or_default().trim(),
            )?;
        } else {
            write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
        }

        Ok(())
    }
}

impl std::fmt::Debug for VersionInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
            self.crate_name, self.major, self.minor, self.patch,
        )?;
        if self.commit_hash.is_some() {
            write!(
                f,
                ", commit_hash: \"{}\", commit_date: \"{}\" }}",
                self.commit_hash.clone().unwrap_or_default().trim(),
                self.commit_date.clone().unwrap_or_default().trim()
            )?;
        } else {
            write!(f, " }}")?;
        }

        Ok(())
    }
}

pub fn get_channel() -> Option<String> {
    if let Ok(channel) = env::var("CFG_RELEASE_CHANNEL") {
        Some(channel)
    } else {
        // we could ask ${RUSTC} -Vv and do some parsing and find out
        Some(String::from("nightly"))
    }
}

pub fn get_commit_hash() -> Option<String> {
    std::process::Command::new("git")
        .args(&["rev-parse", "--short", "HEAD"])
        .output()
        .ok()
        .and_then(|r| String::from_utf8(r.stdout).ok())
}

pub fn get_commit_date() -> Option<String> {
    std::process::Command::new("git")
        .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
        .output()
        .ok()
        .and_then(|r| String::from_utf8(r.stdout).ok())
}

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

    #[test]
    fn test_struct_local() {
        let vi = get_version_info!();
        assert_eq!(vi.major, 0);
        assert_eq!(vi.minor, 1);
        assert_eq!(vi.patch, 0);
        assert_eq!(vi.crate_name, "rustc_tools_util");
        // hard to make positive tests for these since they will always change
        assert!(vi.commit_hash.is_none());
        assert!(vi.commit_date.is_none());
    }

    #[test]
    fn test_display_local() {
        let vi = get_version_info!();
        assert_eq!(vi.to_string(), "rustc_tools_util 0.1.0");
    }

    #[test]
    fn test_debug_local() {
        let vi = get_version_info!();
        let s = format!("{:?}", vi);
        assert_eq!(
            s,
            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 1, patch: 0 }"
        );
    }

}
<|endoftext|>


<commit_before>use diesel::*;
use std::sync::ONCE_INIT;

use super::OwnerTeamsResponse;
use builders::{CrateBuilder, PublishBuilder};
use models::{Crate, NewUser};
use record::GhUser;
use {add_team_to_crate, new_team, OkBool, RequestHelper, TestApp};

impl ::util::MockCookieUser {
    /// Add to the specified crate the specified owner.
    fn add_crate_owner(&self, krate_name: &str, owner: &str) -> ::util::Response<OkBool> {
        let url = format!("/api/v1/crates/{}/owners", krate_name);
        let body = format!("{{\"users\":[\"{}\"]}}", owner);
        self.put(&url, body.as_bytes())
    }

    /// Remove from the specified crate the specified owner.
    fn remove_crate_owner(&self, krate_name: &str, owner: &str) -> ::util::Response<OkBool> {
        let url = format!("/api/v1/crates/{}/owners", krate_name);
        let body = format!("{{\"users\":[\"{}\"]}}", owner);
        self.delete_with_body(&url, body.as_bytes())
    }
}

impl ::util::MockAnonymousUser {
    /// List the team owners of the specified crate.
    fn crate_owner_teams(&self, krate_name: &str) -> ::util::Response<OwnerTeamsResponse> {
        let url = format!("/api/v1/crates/{}/owner_team", krate_name);
        self.get(&url)
    }
}

// Users: `crates-tester-1` and `crates-tester-2`
// Passwords: ask acrichto or gankro
// Teams: `crates-test-org:core`, `crates-test-org:just-for-crates-2`
// tester-1 is on core only, tester-2 is on both

static GH_USER_1: GhUser = GhUser {
    login: "crates-tester-1",
    init: ONCE_INIT,
};
static GH_USER_2: GhUser = GhUser {
    login: "crates-tester-2",
    init: ONCE_INIT,
};

fn mock_user_on_only_x() -> NewUser<'static> {
    GH_USER_1.user()
}
fn mock_user_on_x_and_y() -> NewUser<'static> {
    GH_USER_2.user()
}

// Test adding team without `github:`
#[test]
fn not_github() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_not_github", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_not_github", "dropbox:foo:foo")
        .bad_with_status(200);

    assert!(
        json.errors[0].detail.contains("unknown organization"),
        "{:?}",
        json.errors
    );
}

#[test]
fn weird_name() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_weird_name", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_weird_name", "github:foo/../bar:wut")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("organization cannot contain"),
        "{:?}",
        json.errors
    );
}

// Test adding team without second `:`
#[test]
fn one_colon() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_one_colon", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_one_colon", "github:foo")
        .bad_with_status(200);

    assert!(
        json.errors[0].detail.contains("missing github team"),
        "{:?}",
        json.errors
    );
}

#[test]
fn nonexistent_team() {
    let (app, _, user) = TestApp::with_proxy().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_nonexistent", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner(
            "foo_nonexistent",
            "github:crates-test-org:this-does-not-exist",
        ).bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("could not find the github team crates-test-org/this-does-not-exist"),
        "{:?}",
        json.errors
    );
}

// Test adding team names with mixed case, when on the team
#[test]
fn add_team_mixed_case() {
    let (app, anon) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_mixed_case", user.as_model().id).expect_build(conn);
    });

    user.add_crate_owner("foo_mixed_case", "github:Crates-Test-Org:Core")
        .good();

    app.db(|conn| {
        let krate = Crate::by_name("foo_mixed_case")
            .first::<Crate>(conn)
            .unwrap();
        let owners = krate.owners(conn).unwrap();
        assert_eq!(owners.len(), 2);
        let owner = &owners[1];
        assert_eq!(owner.login(), owner.login().to_lowercase());
    });

    let json = anon.crate_owner_teams("foo_mixed_case").good();
    assert_eq!(json.teams.len(), 1);
    assert_eq!(json.teams[0].login, "github:crates-test-org:core");
}

// Test adding team as owner when not on it
#[test]
fn add_team_as_non_member() {
    let (app, _) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_only_x().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_team_non_member", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner(
            "foo_team_non_member",
            "github:crates-test-org:just-for-crates-2",
        ).bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("only members of a team can add it as an owner"),
        "{:?}",
        json.errors
    );
}

#[test]
fn remove_team_as_named_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_remove_team", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_remove_team", "github:crates-test-org:core")
        .good();

    user_on_both_teams
        .remove_crate_owner("foo_remove_team", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_x().gh_login);
    let crate_to_publish = PublishBuilder::new("foo_remove_team").version("2.0.0");
    let json = user_on_one_team
        .publish(crate_to_publish)
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("this crate exists but you don't seem to be an owner.",),
        "{:?}",
        json.errors
    );
}

#[test]
fn remove_team_as_team_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_remove_team_owner", user_on_both_teams.as_model().id)
            .expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_remove_team_owner", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_x().gh_login);

    let json = user_on_one_team
        .remove_crate_owner("foo_remove_team_owner", "github:crates-test-org:core")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("team members don't have permission to modify owners",),
        "{:?}",
        json.errors
    );
}

// Test trying to publish a crate we don't own
#[test]
fn publish_not_owned() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_not_owned", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_not_owned", "github:crates-test-org:just-for-crates-2")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_x().gh_login);

    let crate_to_publish = PublishBuilder::new("foo_not_owned").version("2.0.0");
    let json = user_on_one_team
        .publish(crate_to_publish)
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("this crate exists but you don't seem to be an owner.",),
        "{:?}",
        json.errors
    );
}

// Test trying to publish a krate we do own (but only because of teams)
#[test]
fn publish_owned() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_team_owned", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_team_owned", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_x().gh_login);

    let crate_to_publish = PublishBuilder::new("foo_team_owned").version("2.0.0");
    user_on_one_team.publish(crate_to_publish).good();
}

// Test trying to change owners (when only on an owning team)
#[test]
fn add_owners_as_team_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_x_and_y().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_add_owner", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_add_owner", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_x().gh_login);

    let json = user_on_one_team
        .add_crate_owner("foo_add_owner", "arbitrary_username")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("team members don't have permission to modify owners",),
        "{:?}",
        json.errors
    );
}

#[test]
fn crates_by_team_id() {
    let (app, anon, user) = TestApp::init().with_user();
    let user = user.as_model();

    let team = app.db(|conn| {
        let t = new_team("github:crates-test-org:team_foo")
            .create_or_update(conn)
            .unwrap();
        let krate = CrateBuilder::new("foo", user.id).expect_build(conn);
        add_team_to_crate(&t, &krate, &user, conn).unwrap();
        t
    });

    let json = anon.search(&format!("team_id={}", team.id));
    assert_eq!(json.crates.len(), 1);
}

#[test]
fn crates_by_team_id_not_including_deleted_owners() {
    // This needs to use the proxy beacuse removing a team checks with github that you're on the
    // team before you're allowed to remove it from the crate
    let (app, anon) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_x_and_y().gh_login);
    let user = user.as_model();

    let team = app.db(|conn| {
        let t = new_team("github:crates-test-org:core")
            .create_or_update(conn)
            .unwrap();
        let krate = CrateBuilder::new("foo", user.id).expect_build(conn);
        add_team_to_crate(&t, &krate, &user, conn).unwrap();
        krate
            .owner_remove(&app.as_inner(), conn, &user, &t.login)
            .unwrap();
        t
    });

    let json = anon.search(&format!("team_id={}", team.id));
    assert_eq!(json.crates.len(), 0);
}
<commit_msg>Rename these functions so I don't have to think about x and y again<commit_after>use diesel::*;
use std::sync::ONCE_INIT;

use super::OwnerTeamsResponse;
use builders::{CrateBuilder, PublishBuilder};
use models::{Crate, NewUser};
use record::GhUser;
use {add_team_to_crate, new_team, OkBool, RequestHelper, TestApp};

impl ::util::MockCookieUser {
    /// Add to the specified crate the specified owner.
    fn add_crate_owner(&self, krate_name: &str, owner: &str) -> ::util::Response<OkBool> {
        let url = format!("/api/v1/crates/{}/owners", krate_name);
        let body = format!("{{\"users\":[\"{}\"]}}", owner);
        self.put(&url, body.as_bytes())
    }

    /// Remove from the specified crate the specified owner.
    fn remove_crate_owner(&self, krate_name: &str, owner: &str) -> ::util::Response<OkBool> {
        let url = format!("/api/v1/crates/{}/owners", krate_name);
        let body = format!("{{\"users\":[\"{}\"]}}", owner);
        self.delete_with_body(&url, body.as_bytes())
    }
}

impl ::util::MockAnonymousUser {
    /// List the team owners of the specified crate.
    fn crate_owner_teams(&self, krate_name: &str) -> ::util::Response<OwnerTeamsResponse> {
        let url = format!("/api/v1/crates/{}/owner_team", krate_name);
        self.get(&url)
    }
}

// Users: `crates-tester-1` and `crates-tester-2`
// Passwords: ask acrichto or gankro
// Teams: `crates-test-org:core`, `crates-test-org:just-for-crates-2`
// tester-1 is on core only, tester-2 is on both

static GH_USER_1: GhUser = GhUser {
    login: "crates-tester-1",
    init: ONCE_INIT,
};
static GH_USER_2: GhUser = GhUser {
    login: "crates-tester-2",
    init: ONCE_INIT,
};

fn mock_user_on_only_one_team() -> NewUser<'static> {
    GH_USER_1.user()
}
fn mock_user_on_both_teams() -> NewUser<'static> {
    GH_USER_2.user()
}

// Test adding team without `github:`
#[test]
fn not_github() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_not_github", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_not_github", "dropbox:foo:foo")
        .bad_with_status(200);

    assert!(
        json.errors[0].detail.contains("unknown organization"),
        "{:?}",
        json.errors
    );
}

#[test]
fn weird_name() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_weird_name", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_weird_name", "github:foo/../bar:wut")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("organization cannot contain"),
        "{:?}",
        json.errors
    );
}

// Test adding team without second `:`
#[test]
fn one_colon() {
    let (app, _, user) = TestApp::init().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_one_colon", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner("foo_one_colon", "github:foo")
        .bad_with_status(200);

    assert!(
        json.errors[0].detail.contains("missing github team"),
        "{:?}",
        json.errors
    );
}

#[test]
fn nonexistent_team() {
    let (app, _, user) = TestApp::with_proxy().with_user();

    app.db(|conn| {
        CrateBuilder::new("foo_nonexistent", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner(
            "foo_nonexistent",
            "github:crates-test-org:this-does-not-exist",
        ).bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("could not find the github team crates-test-org/this-does-not-exist"),
        "{:?}",
        json.errors
    );
}

// Test adding team names with mixed case, when on the team
#[test]
fn add_team_mixed_case() {
    let (app, anon) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_mixed_case", user.as_model().id).expect_build(conn);
    });

    user.add_crate_owner("foo_mixed_case", "github:Crates-Test-Org:Core")
        .good();

    app.db(|conn| {
        let krate = Crate::by_name("foo_mixed_case")
            .first::<Crate>(conn)
            .unwrap();
        let owners = krate.owners(conn).unwrap();
        assert_eq!(owners.len(), 2);
        let owner = &owners[1];
        assert_eq!(owner.login(), owner.login().to_lowercase());
    });

    let json = anon.crate_owner_teams("foo_mixed_case").good();
    assert_eq!(json.teams.len(), 1);
    assert_eq!(json.teams[0].login, "github:crates-test-org:core");
}

// Test adding team as owner when not on it
#[test]
fn add_team_as_non_member() {
    let (app, _) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_only_one_team().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_team_non_member", user.as_model().id).expect_build(conn);
    });

    let json = user
        .add_crate_owner(
            "foo_team_non_member",
            "github:crates-test-org:just-for-crates-2",
        ).bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("only members of a team can add it as an owner"),
        "{:?}",
        json.errors
    );
}

#[test]
fn remove_team_as_named_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_remove_team", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_remove_team", "github:crates-test-org:core")
        .good();

    user_on_both_teams
        .remove_crate_owner("foo_remove_team", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_one_team().gh_login);
    let crate_to_publish = PublishBuilder::new("foo_remove_team").version("2.0.0");
    let json = user_on_one_team
        .publish(crate_to_publish)
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("this crate exists but you don't seem to be an owner.",),
        "{:?}",
        json.errors
    );
}

#[test]
fn remove_team_as_team_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_remove_team_owner", user_on_both_teams.as_model().id)
            .expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_remove_team_owner", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_one_team().gh_login);

    let json = user_on_one_team
        .remove_crate_owner("foo_remove_team_owner", "github:crates-test-org:core")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("team members don't have permission to modify owners",),
        "{:?}",
        json.errors
    );
}

// Test trying to publish a crate we don't own
#[test]
fn publish_not_owned() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_not_owned", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_not_owned", "github:crates-test-org:just-for-crates-2")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_one_team().gh_login);

    let crate_to_publish = PublishBuilder::new("foo_not_owned").version("2.0.0");
    let json = user_on_one_team
        .publish(crate_to_publish)
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("this crate exists but you don't seem to be an owner.",),
        "{:?}",
        json.errors
    );
}

// Test trying to publish a krate we do own (but only because of teams)
#[test]
fn publish_owned() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_team_owned", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_team_owned", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_one_team().gh_login);

    let crate_to_publish = PublishBuilder::new("foo_team_owned").version("2.0.0");
    user_on_one_team.publish(crate_to_publish).good();
}

// Test trying to change owners (when only on an owning team)
#[test]
fn add_owners_as_team_owner() {
    let (app, _) = TestApp::with_proxy().empty();
    let user_on_both_teams = app.db_new_user(&mock_user_on_both_teams().gh_login);

    app.db(|conn| {
        CrateBuilder::new("foo_add_owner", user_on_both_teams.as_model().id).expect_build(conn);
    });

    user_on_both_teams
        .add_crate_owner("foo_add_owner", "github:crates-test-org:core")
        .good();

    let user_on_one_team = app.db_new_user(&mock_user_on_only_one_team().gh_login);

    let json = user_on_one_team
        .add_crate_owner("foo_add_owner", "arbitrary_username")
        .bad_with_status(200);

    assert!(
        json.errors[0]
            .detail
            .contains("team members don't have permission to modify owners",),
        "{:?}",
        json.errors
    );
}

#[test]
fn crates_by_team_id() {
    let (app, anon, user) = TestApp::init().with_user();
    let user = user.as_model();

    let team = app.db(|conn| {
        let t = new_team("github:crates-test-org:team_foo")
            .create_or_update(conn)
            .unwrap();
        let krate = CrateBuilder::new("foo", user.id).expect_build(conn);
        add_team_to_crate(&t, &krate, &user, conn).unwrap();
        t
    });

    let json = anon.search(&format!("team_id={}", team.id));
    assert_eq!(json.crates.len(), 1);
}

#[test]
fn crates_by_team_id_not_including_deleted_owners() {
    // This needs to use the proxy beacuse removing a team checks with github that you're on the
    // team before you're allowed to remove it from the crate
    let (app, anon) = TestApp::with_proxy().empty();
    let user = app.db_new_user(&mock_user_on_both_teams().gh_login);
    let user = user.as_model();

    let team = app.db(|conn| {
        let t = new_team("github:crates-test-org:core")
            .create_or_update(conn)
            .unwrap();
        let krate = CrateBuilder::new("foo", user.id).expect_build(conn);
        add_team_to_crate(&t, &krate, &user, conn).unwrap();
        krate
            .owner_remove(&app.as_inner(), conn, &user, &t.login)
            .unwrap();
        t
    });

    let json = anon.search(&format!("team_id={}", team.id));
    assert_eq!(json.crates.len(), 0);
}
<|endoftext|>


<commit_before>use std::ffi::CString;
use std::convert::Into;

use rustc::lib::llvm;
use rustc::lib::llvm::{ ContextRef
                      , ModuleRef
                      , ValueRef
                      , BuilderRef
                  };
use seax::compiler_tools::ForkTable;

use position::Positional;
use ast::{Form, DefForm};

pub type IRResult = Result<ValueRef, Positional<String>>;
pub type SymbolTable<'a> = ForkTable<'a, &'a str, ValueRef>;

/// Trait for that which may join in The Great Work
pub trait Compile {
    fn to_ir(&self, context: LLVMContext) -> IRResult;
}

/// LLVM compilation context.
///
/// This is based rather loosely on MIT License code from
/// the [iron-kaleidoscope](https://github.com/jauhien/iron-kaleidoscope)
/// tutorial, and from [`librustc_trans`](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/trans/mod.rs)
/// from the Rust compiler.
pub struct LLVMContext<'a> {
    pub llctx: ContextRef,
    pub llmod: ModuleRef,
    pub llbuilder: BuilderRef,
    pub names: SymbolTable<'a>
}

// because we are in the Raw Pointer Sadness Zone (read: unsafe),
// it is necessary that we assert that everything exists.
macro_rules! not_null {
    ($target:expr) => ({
        let e = $target;
        if e.is_null() {
            panic!("assertion failed: {} returned null!", stringify!($target));
        } else { e }
    })
}

impl<'a> LLVMContext<'a> {

    pub fn new(module_name: &str) -> Self {
        let name = CString::new(module_name).unwrap();
        unsafe {
            let ctx = not_null!(llvm::LLVMContextCreate());
            LLVMContext {
                llctx: ctx
              , llmod: not_null!(llvm::LLVMModuleCreateWithNameInContext(name.into_raw(), ctx))
              , llbuilder: not_null!(llvm::LLVMCreateBuilderInContext(ctx))
              , names: SymbolTable::new()
            }
        }
    }

    /// Dump the module's contents to stderr for debugging
    ///
    /// Apparently this is the only reasonable way to get a textual
    /// representation of a `ModuleRef` in `librustc_llvm`...
    pub fn dump(&self) {
        unsafe { llvm::LLVMDumpModule(self.llmod); }
    }
}

impl<'a> Drop for LLVMContext<'a> {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMDisposeModule(self.llmod);
            llvm::LLVMDisposeBuilder(self.llbuilder);
            llvm::LLVMContextDispose(self.llctx);
        }
    }
}

impl Compile for Form {
    fn to_ir(&self, context: LLVMContext) -> IRResult {
        match self {
            &Form::Define(ref form) => form.to_ir(context)
        }
    }
}

impl Compile for DefForm {
    fn to_ir(&self, context: LLVMContext) -> IRResult {
        match *self {
            DefForm::TopLevel { ref name, ref annot, ref value } =>
                unimplemented!(),
            DefForm::Function { ref name, ref annot, ref formals, ref body } =>
                unsafe {
                    let name_ptr // function name as C string pointer
                        = CString::new((&name.value).clone()).unwrap().as_ptr();
                    let prev_decl // check LLVM module for previous declaration
                        = llvm::LLVMGetNamedFunction(context.llmod, name_ptr);

                    if !prev_decl.is_null() { // a previous declaration exists
                        unimplemented!() // TODO: overloading rules happen here
                    } else { // new function declaration
                        unimplemented!()
                    }
                }
        }
    }
}
<commit_msg>Don't need to bind some names in patterns (These will be in type states)<commit_after>use std::ffi::CString;
use std::convert::Into;

use rustc::lib::llvm;
use rustc::lib::llvm::{ ContextRef
                      , ModuleRef
                      , ValueRef
                      , BuilderRef
                  };
use seax::compiler_tools::ForkTable;

use position::Positional;
use ast::{Form, DefForm};

pub type IRResult = Result<ValueRef, Positional<String>>;
pub type SymbolTable<'a> = ForkTable<'a, &'a str, ValueRef>;

/// Trait for that which may join in The Great Work
pub trait Compile {
    fn to_ir(&self, context: LLVMContext) -> IRResult;
}

/// LLVM compilation context.
///
/// This is based rather loosely on MIT License code from
/// the [iron-kaleidoscope](https://github.com/jauhien/iron-kaleidoscope)
/// tutorial, and from [`librustc_trans`](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/trans/mod.rs)
/// from the Rust compiler.
pub struct LLVMContext<'a> {
    pub llctx: ContextRef,
    pub llmod: ModuleRef,
    pub llbuilder: BuilderRef,
    pub names: SymbolTable<'a>
}

// because we are in the Raw Pointer Sadness Zone (read: unsafe),
// it is necessary that we assert that everything exists.
macro_rules! not_null {
    ($target:expr) => ({
        let e = $target;
        if e.is_null() {
            panic!("assertion failed: {} returned null!", stringify!($target));
        } else { e }
    })
}

impl<'a> LLVMContext<'a> {

    pub fn new(module_name: &str) -> Self {
        let name = CString::new(module_name).unwrap();
        unsafe {
            let ctx = not_null!(llvm::LLVMContextCreate());
            LLVMContext {
                llctx: ctx
              , llmod: not_null!(llvm::LLVMModuleCreateWithNameInContext(name.into_raw(), ctx))
              , llbuilder: not_null!(llvm::LLVMCreateBuilderInContext(ctx))
              , names: SymbolTable::new()
            }
        }
    }

    /// Dump the module's contents to stderr for debugging
    ///
    /// Apparently this is the only reasonable way to get a textual
    /// representation of a `ModuleRef` in `librustc_llvm`...
    pub fn dump(&self) {
        unsafe { llvm::LLVMDumpModule(self.llmod); }
    }
}

impl<'a> Drop for LLVMContext<'a> {
    fn drop(&mut self) {
        unsafe {
            llvm::LLVMDisposeModule(self.llmod);
            llvm::LLVMDisposeBuilder(self.llbuilder);
            llvm::LLVMContextDispose(self.llctx);
        }
    }
}

impl Compile for Form {
    fn to_ir(&self, context: LLVMContext) -> IRResult {
        match self {
            &Form::Define(ref form) => form.to_ir(context)
        }
    }
}

impl Compile for DefForm {
    fn to_ir(&self, context: LLVMContext) -> IRResult {
        match *self {
            DefForm::TopLevel { ref name, ref value, .. } =>
                unimplemented!(),
            DefForm::Function { ref name, ref body, .. } =>
                unsafe {
                    let name_ptr // function name as C string pointer
                        = CString::new((&name.value).clone()).unwrap().as_ptr();
                    let prev_decl // check LLVM module for previous declaration
                        = llvm::LLVMGetNamedFunction(context.llmod, name_ptr);

                    if !prev_decl.is_null() { // a previous declaration exists
                        unimplemented!() // TODO: overloading rules happen here
                    } else { // new function declaration
                        unimplemented!()
                    }
                }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! This module contains the `ensure_image_view_compatible` function, which verifies whether
//! an image view can be used as a render pass attachment.

use std::error;
use std::fmt;
use format::Format;
use framebuffer::RenderPassDesc;
use image::ImageViewAccess;

/// Checks whether the given image view is allowed to be the nth attachment of the given render
/// pass.
///
/// # Panic
///
/// Panicks if the attachment number is out of range.
// TODO: add a specializable trait instead, that uses this function
// TODO: ImageView instead of ImageViewAccess?
pub fn ensure_image_view_compatible<Rp, I>(render_pass: &Rp, attachment_num: usize, image: &I)
                                           -> Result<(), IncompatibleRenderPassAttachmentError>
    where Rp: ?Sized + RenderPassDesc,
          I: ?Sized + ImageViewAccess
{
    let attachment_desc = render_pass.attachment(attachment_num)
                                     .expect("Attachment num out of range");

    if image.format() != attachment_desc.format {
        return Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
            expected: attachment_desc.format,
            obtained: image.format(),
        });
    }

    if image.samples() != attachment_desc.samples {
        return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch {
            expected: attachment_desc.samples,
            obtained: image.samples(),
        });
    }

    if !image.identity_swizzle() {
        return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled);
    }

    for subpass_num in 0 .. render_pass.num_subpasses() {
        let subpass = render_pass.subpass(subpass_num).expect("Subpass num out of range ; \
                                                               wrong RenderPassDesc trait impl");

        if subpass.color_attachments.iter().any(|&(n, _)| n == attachment_num) {
            debug_assert!(image.parent().has_color());  // Was normally checked by the render pass.
            if !image.parent().inner().usage_color_attachment() {
                return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
            }
        }

        if let Some((ds, _)) = subpass.depth_stencil {
            if ds == attachment_num {
                // Was normally checked by the render pass.
                debug_assert!(image.parent().has_depth() || image.parent().has_stencil());
                if !image.parent().inner().usage_depth_stencil_attachment() {
                    return Err(IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage);
                }
            }
        }

        if subpass.input_attachments.iter().any(|&(n, _)| n == attachment_num) {
            if !image.parent().inner().usage_input_attachment() {
                return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
            }
        }
    }

    // TODO: consider forbidding LoadOp::Load if image is transient

    // TODO: are all image layouts allowed? check this

    Ok(())
}

/// Error that can happen when an image is not compatible with a render pass attachment slot.
#[derive(Copy, Clone, Debug)]
pub enum IncompatibleRenderPassAttachmentError {
    /// The image format expected by the render pass doesn't match the actual format of
    /// the image.
    FormatMismatch {
        /// Format expected by the render pass.
        expected: Format,
        /// Format of the image.
        obtained: Format,
    },

    /// The number of samples expected by the render pass doesn't match the number of samples of
    /// the image.
    SamplesMismatch {
        /// Number of samples expected by the render pass.
        expected: u32,
        /// Number of samples of the image.
        obtained: u32,
    },

    /// The image view has a component swizzle that is different from identity.
    NotIdentitySwizzled,

    /// The image is used as a color attachment but is missing the color attachment usage.
    MissingColorAttachmentUsage,

    /// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment
    /// usage.
    MissingDepthStencilAttachmentUsage,

    /// The image is used as an input attachment but is missing the input attachment usage.
    MissingInputAttachmentUsage,
}

impl error::Error for IncompatibleRenderPassAttachmentError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => {
                "mismatch between the format expected by the render pass and the actual format"
            },
            IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => {
                "mismatch between the number of samples expected by the render pass and the actual \
                 number of samples"
            },
            IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => {
                "the image view does not use identity swizzling"
            },
            IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => {
                "the image is used as a color attachment but is missing the color attachment usage"
            },
            IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => {
                "the image is used as a depth/stencil attachment but is missing the depth-stencil \
                 attachment usage"
            },
            IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => {
                "the image is used as an input attachment but is missing the input \
                 attachment usage"
            },
        }
    }
}

impl fmt::Display for IncompatibleRenderPassAttachmentError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use format::Format;
    use image::AttachmentImage;
    use image::ImageView;
    use super::ensure_image_view_compatible;
    use super::IncompatibleRenderPassAttachmentError;

    #[test]
    fn basic_ok() {
        let (device, _) = gfx_dev_and_queue!();

        let rp = Arc::new(single_pass_renderpass!(device.clone(),
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: Format::R8G8B8A8Unorm,
                    samples: 1,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        ).unwrap());

        let img = AttachmentImage::new(&device, [128, 128], Format::R8G8B8A8Unorm).unwrap();

        ensure_image_view_compatible(&rp, 0, &img.access()).unwrap();
    }

    #[test]
    fn format_mismatch() {
        let (device, _) = gfx_dev_and_queue!();

        let rp = Arc::new(single_pass_renderpass!(device.clone(),
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: Format::R16G16Sfloat,
                    samples: 1,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        ).unwrap());

        let img = AttachmentImage::new(&device, [128, 128], Format::R8G8B8A8Unorm).unwrap();

        match ensure_image_view_compatible(&rp, 0, &img.access()) {
            Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
                expected: Format::R16G16Sfloat, obtained: Format::R8G8B8A8Unorm }) => (),
            e => panic!("{:?}", e)
        }
    }

    // TODO: more tests
}
<commit_msg>Minor work on compat_atch tests<commit_after>// Copyright (c) 2017 The vulkano developers
// 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! This module contains the `ensure_image_view_compatible` function, which verifies whether
//! an image view can be used as a render pass attachment.

use std::error;
use std::fmt;
use format::Format;
use framebuffer::RenderPassDesc;
use image::ImageViewAccess;

/// Checks whether the given image view is allowed to be the nth attachment of the given render
/// pass.
///
/// # Panic
///
/// Panicks if the attachment number is out of range.
// TODO: add a specializable trait instead, that uses this function
// TODO: ImageView instead of ImageViewAccess?
pub fn ensure_image_view_compatible<Rp, I>(render_pass: &Rp, attachment_num: usize, image: &I)
                                           -> Result<(), IncompatibleRenderPassAttachmentError>
    where Rp: ?Sized + RenderPassDesc,
          I: ?Sized + ImageViewAccess
{
    let attachment_desc = render_pass.attachment(attachment_num)
                                     .expect("Attachment num out of range");

    if image.format() != attachment_desc.format {
        return Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
            expected: attachment_desc.format,
            obtained: image.format(),
        });
    }

    if image.samples() != attachment_desc.samples {
        return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch {
            expected: attachment_desc.samples,
            obtained: image.samples(),
        });
    }

    if !image.identity_swizzle() {
        return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled);
    }

    for subpass_num in 0 .. render_pass.num_subpasses() {
        let subpass = render_pass.subpass(subpass_num).expect("Subpass num out of range ; \
                                                               wrong RenderPassDesc trait impl");

        if subpass.color_attachments.iter().any(|&(n, _)| n == attachment_num) {
            debug_assert!(image.parent().has_color());  // Was normally checked by the render pass.
            if !image.parent().inner().usage_color_attachment() {
                return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
            }
        }

        if let Some((ds, _)) = subpass.depth_stencil {
            if ds == attachment_num {
                // Was normally checked by the render pass.
                debug_assert!(image.parent().has_depth() || image.parent().has_stencil());
                if !image.parent().inner().usage_depth_stencil_attachment() {
                    return Err(IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage);
                }
            }
        }

        if subpass.input_attachments.iter().any(|&(n, _)| n == attachment_num) {
            if !image.parent().inner().usage_input_attachment() {
                return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
            }
        }
    }

    // TODO: consider forbidding LoadOp::Load if image is transient

    // TODO: are all image layouts allowed? check this

    Ok(())
}

/// Error that can happen when an image is not compatible with a render pass attachment slot.
#[derive(Copy, Clone, Debug)]
pub enum IncompatibleRenderPassAttachmentError {
    /// The image format expected by the render pass doesn't match the actual format of
    /// the image.
    FormatMismatch {
        /// Format expected by the render pass.
        expected: Format,
        /// Format of the image.
        obtained: Format,
    },

    /// The number of samples expected by the render pass doesn't match the number of samples of
    /// the image.
    SamplesMismatch {
        /// Number of samples expected by the render pass.
        expected: u32,
        /// Number of samples of the image.
        obtained: u32,
    },

    /// The image view has a component swizzle that is different from identity.
    NotIdentitySwizzled,

    /// The image is used as a color attachment but is missing the color attachment usage.
    MissingColorAttachmentUsage,

    /// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment
    /// usage.
    MissingDepthStencilAttachmentUsage,

    /// The image is used as an input attachment but is missing the input attachment usage.
    MissingInputAttachmentUsage,
}

impl error::Error for IncompatibleRenderPassAttachmentError {
    #[inline]
    fn description(&self) -> &str {
        match *self {
            IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => {
                "mismatch between the format expected by the render pass and the actual format"
            },
            IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => {
                "mismatch between the number of samples expected by the render pass and the actual \
                 number of samples"
            },
            IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => {
                "the image view does not use identity swizzling"
            },
            IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => {
                "the image is used as a color attachment but is missing the color attachment usage"
            },
            IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => {
                "the image is used as a depth/stencil attachment but is missing the depth-stencil \
                 attachment usage"
            },
            IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => {
                "the image is used as an input attachment but is missing the input \
                 attachment usage"
            },
        }
    }
}

impl fmt::Display for IncompatibleRenderPassAttachmentError {
    #[inline]
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

#[cfg(test)]
mod tests {
    use format::Format;
    use framebuffer::EmptySinglePassRenderPassDesc;
    use image::AttachmentImage;
    use image::ImageView;
    use super::ensure_image_view_compatible;
    use super::IncompatibleRenderPassAttachmentError;

    #[test]
    fn basic_ok() {
        let (device, _) = gfx_dev_and_queue!();

        let rp = single_pass_renderpass!(device.clone(),
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: Format::R8G8B8A8Unorm,
                    samples: 1,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        ).unwrap();

        let img = AttachmentImage::new(&device, [128, 128], Format::R8G8B8A8Unorm).unwrap();

        ensure_image_view_compatible(&rp, 0, &img.access()).unwrap();
    }

    #[test]
    fn format_mismatch() {
        let (device, _) = gfx_dev_and_queue!();

        let rp = single_pass_renderpass!(device.clone(),
            attachments: {
                color: {
                    load: Clear,
                    store: Store,
                    format: Format::R16G16Sfloat,
                    samples: 1,
                }
            },
            pass: {
                color: [color],
                depth_stencil: {}
            }
        ).unwrap();

        let img = AttachmentImage::new(&device, [128, 128], Format::R8G8B8A8Unorm).unwrap();

        match ensure_image_view_compatible(&rp, 0, &img.access()) {
            Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
                expected: Format::R16G16Sfloat, obtained: Format::R8G8B8A8Unorm }) => (),
            e => panic!("{:?}", e)
        }
    }

    #[test]
    #[should_panic(expected = "Attachment num out of range")]
    fn attachment_out_of_range() {
        let (device, _) = gfx_dev_and_queue!();

        let rp = EmptySinglePassRenderPassDesc;
        let img = AttachmentImage::new(&device, [128, 128], Format::R8G8B8A8Unorm).unwrap();

        let _ = ensure_image_view_compatible(&rp, 0, &img.access());
    }

    // TODO: more tests
}
<|endoftext|>


<commit_before>use rustbox::keyboard::Key;
use rustbox::{RustBox};
use time::Tm;

use tui::messaging::MessagingUI;
use tui::MsgTarget;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet};

use utils::opt_to_vec;

// TODO: How to reorder tabs?
// TODO: How to report errors?

pub struct Tabbed {
    tabs       : Vec<Tab>,
    active_idx : Option<usize>,
    width      : i32,
    height     : i32,
}

struct Tab {
    widget : MessagingUI,
    src    : MsgSource,
}

/// TUI source of a message from the user.
#[derive(Debug, Clone)]
pub enum MsgSource {
    /// Message sent to a server tab.
    Serv { serv_name : String },

    /// Message sent to a channel tab.
    Chan { serv_name : String, chan_name : String },

    /// Message sent to a privmsg tab.
    User { serv_name : String, nick : String },
}

impl MsgSource {
    pub fn serv_name<'a>(&'a self) -> &'a str {
        match self {
            &MsgSource::Serv { ref serv_name } => serv_name,
            &MsgSource::Chan { ref serv_name, .. } => serv_name,
            &MsgSource::User { ref serv_name, .. } => serv_name,
        }
    }
}

impl Tab {
    pub fn visible_name<'a>(&'a self) -> &'a str {
        match &self.src {
            &MsgSource::Serv { ref serv_name, .. } => serv_name,
            &MsgSource::Chan { ref chan_name, .. } => chan_name,
            &MsgSource::User { ref nick, .. } => nick,
        }
    }
}

pub enum TabbedRet<'t> {
    KeyHandled,
    KeyIgnored,

    Input {
        msg  : Vec<char>,
        from : &'t MsgSource,
    },
}

impl Tabbed {
    pub fn new(width : i32, height : i32) -> Tabbed {
        Tabbed {
            tabs: Vec::new(),
            active_idx: None,
            width: width,
            height: height,
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_server_tab(&mut self, serv_name : &str) -> Option<usize> {
        match self.find_serv_tab_idx(&serv_name) {
            None => {
                self.tabs.push(Tab {
                    widget: MessagingUI::new(self.width, self.height - 1),
                    src: MsgSource::Serv { serv_name: serv_name.to_owned() },
                });
                self.active_idx = Some(self.tabs.len() - 1);
                Some(self.tabs.len() - 1)
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_chan_tab(&mut self, serv_name : &str, chan_name : &str) -> Option<usize> {
        match self.find_chan_tab_idx(&serv_name, &chan_name) {
            None => {
                match self.find_last_serv_tab_idx(&serv_name) {
                    None => {
                        self.new_server_tab(serv_name);
                        self.new_chan_tab(serv_name, chan_name)
                    },
                    Some(tab_idx) => {
                        self.tabs.insert(tab_idx + 1, Tab {
                            widget: MessagingUI::new(self.width, self.height - 1),
                            src: MsgSource::Chan { serv_name: serv_name.to_owned(),
                                                   chan_name: chan_name.to_owned() },
                        });
                        self.active_idx = Some(tab_idx + 1);
                        Some(tab_idx + 1)
                    }
                }
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_user_tab(&mut self, serv_name : &str, nick : &str) -> Option<usize> {
        match self.find_user_tab_idx(serv_name, nick) {
            None => {
                match self.find_last_serv_tab_idx(&serv_name) {
                    None => {
                        self.new_server_tab(serv_name);
                        self.new_user_tab(serv_name, nick)
                    },
                    Some(tab_idx) => {
                        self.tabs.insert(tab_idx + 1, Tab {
                            widget: MessagingUI::new(self.width, self.height - 1),
                            src: MsgSource::User { serv_name: serv_name.to_owned(),
                                                   nick: nick.to_owned() },
                        });
                        self.active_idx = Some(tab_idx + 1);
                        Some(tab_idx + 1)
                    }
                }
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    pub fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        match self.active_idx {
            None => {},
            Some(idx) => self.tabs[idx as usize].widget.draw(rustbox, pos_x, pos_y),
        }

        let mut tab_name_col = 0;
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            if self.active_idx == Some(tab_idx) {
                termbox::print(tab_name_col, self.height - 1,
                               style::TAB_ACTIVE.fg, style::TAB_ACTIVE.bg,
                               tab.visible_name());
            } else {
                termbox::print(tab_name_col, self.height - 1,
                               style::TAB_PASSIVE.fg, style::TAB_PASSIVE.bg,
                               tab.visible_name());
            }
            // len() is OK since sever, chan and nick names are ascii
            tab_name_col += tab.visible_name().len() as i32;
        }
    }

    pub fn keypressed(&mut self, key : Key) -> TabbedRet {
        if key == Key::Tab {
            if self.tabs.len() > 0 {
                self.active_idx = Some((self.active_idx.unwrap() + 1) % self.tabs.len());
            }
            return TabbedRet::KeyHandled;
        }

        match self.active_idx {
            None => TabbedRet::KeyIgnored,
            Some(idx) => {
                match self.tabs[idx as usize].widget.keypressed(key) {
                    WidgetRet::KeyHandled => TabbedRet::KeyHandled,
                    WidgetRet::KeyIgnored => TabbedRet::KeyIgnored,
                    WidgetRet::Input(input) => {
                        TabbedRet::Input {
                            msg: input,
                            from: &self.tabs[idx as usize].src
                        }
                    },
                }
            }
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        for tab in self.tabs.iter_mut() {
            tab.widget.resize(width, height - 1);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Interfacing with tabs

    fn apply_to_target<F>(&mut self, target : &MsgTarget, f : &F)
            where F : Fn(&mut Tab) -> () {

        // Creating a vector just to make borrow checker happy. Borrow checker
        // sucks once more. Here it sucks 2x, I can't even create a Vec<&mut Tab>,
        // I need a Vec<usize>.
        //
        // (I could use an array on stack but whatever)
        let mut target_idxs : Vec<usize> = Vec::with_capacity(1);

        match target {
            &MsgTarget::Server { serv_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::Serv { serv_name: ref serv_name_ } => {
                            if serv_name == serv_name_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::Chan { serv_name, chan_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::Chan { serv_name: ref serv_name_, chan_name: ref chan_name_ } => {
                            if serv_name == serv_name_ && chan_name == chan_name_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::User { serv_name, nick } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::User { serv_name: ref serv_name_, nick: ref nick_ } => {
                            if serv_name == serv_name_ && nick == nick_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::AllServTabs { serv_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    if tab.src.serv_name() == serv_name {
                        target_idxs.push(tab_idx);
                    }
                }
            },

            &MsgTarget::AllTabs => {
                for tab_idx in 0 .. self.tabs.len() {
                    target_idxs.push(tab_idx);
                }
            },

            &MsgTarget::CurrentTab => {
                target_idxs.push(self.active_idx.unwrap());
            },

            &MsgTarget::MultipleTabs(ref targets) => {
                for target in targets.iter() {
                    self.apply_to_target(target, f);
                }
            }
        }

        // Create server/chan/user tab when necessary
        if target_idxs.len() == 0 {
            for idx in self.maybe_create_tab(target) {
                target_idxs.push(idx);
            }
        }

        for tab_idx in target_idxs {
            f(unsafe { self.tabs.get_unchecked_mut(tab_idx) });
        }
    }

    fn maybe_create_tab(&mut self, target : &MsgTarget) -> Vec<usize> {
        match target {
            &MsgTarget::Server { serv_name } => {
                opt_to_vec(self.new_server_tab(serv_name))
            },

            &MsgTarget::Chan { serv_name, chan_name } => {
                opt_to_vec(self.new_chan_tab(serv_name, chan_name))
            },

            &MsgTarget::User { serv_name, nick } => {
                opt_to_vec(self.new_user_tab(serv_name, nick))
            },

            &MsgTarget::MultipleTabs(ref targets) => {
                targets.iter().flat_map(|t : &Box<MsgTarget>| self.maybe_create_tab(&*t)).collect()
            }

            _ => vec![]
        }
    }

    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_client_err_msg(msg);
        });
    }

    #[inline]
    pub fn add_client_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_client_msg(msg);
        });
    }

    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_privmsg(sender, msg, tm);
        });
    }

    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_msg(msg, tm);
        });
    }

    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_err_msg(msg, tm);
        });
    }

    #[inline]
    pub fn set_topic(&mut self, title : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.set_topic(title.to_owned());
        });
    }

    ////////////////////////////////////////////////////////////////////////////
    // Helpers

    fn find_serv_tab_idx(&self, serv_name_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::Serv { ref serv_name } => {
                    if serv_name_ == serv_name {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    fn find_chan_tab_idx(&self, serv_name_ : &str, chan_name_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::Chan { ref serv_name, ref chan_name } => {
                    if serv_name_ == serv_name && chan_name_ == chan_name {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    fn find_user_tab_idx(&self, serv_name_ : &str, nick_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::User { ref serv_name, ref nick } => {
                    if serv_name_ == serv_name && nick_ == nick {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    /// Index of the last tab with the given server name.
    fn find_last_serv_tab_idx(&self, serv_name : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate().rev() {
            if tab.src.serv_name() == serv_name {
                return Some(tab_idx);
            }
        }
        None
    }
}
<commit_msg>fix some resize and draw bugs in tabbed<commit_after>use rustbox::keyboard::Key;
use rustbox::{RustBox};
use time::Tm;

use tui::messaging::MessagingUI;
use tui::MsgTarget;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet};

use utils::opt_to_vec;

// TODO: How to reorder tabs?
// TODO: How to report errors?

pub struct Tabbed {
    tabs       : Vec<Tab>,
    active_idx : Option<usize>,
    width      : i32,
    height     : i32,
}

struct Tab {
    widget : MessagingUI,
    src    : MsgSource,
}

/// TUI source of a message from the user.
#[derive(Debug, Clone)]
pub enum MsgSource {
    /// Message sent to a server tab.
    Serv { serv_name : String },

    /// Message sent to a channel tab.
    Chan { serv_name : String, chan_name : String },

    /// Message sent to a privmsg tab.
    User { serv_name : String, nick : String },
}

impl MsgSource {
    pub fn serv_name<'a>(&'a self) -> &'a str {
        match self {
            &MsgSource::Serv { ref serv_name } => serv_name,
            &MsgSource::Chan { ref serv_name, .. } => serv_name,
            &MsgSource::User { ref serv_name, .. } => serv_name,
        }
    }
}

impl Tab {
    pub fn visible_name<'a>(&'a self) -> &'a str {
        match &self.src {
            &MsgSource::Serv { ref serv_name, .. } => serv_name,
            &MsgSource::Chan { ref chan_name, .. } => chan_name,
            &MsgSource::User { ref nick, .. } => nick,
        }
    }
}

pub enum TabbedRet<'t> {
    KeyHandled,
    KeyIgnored,

    Input {
        msg  : Vec<char>,
        from : &'t MsgSource,
    },
}

impl Tabbed {
    pub fn new(width : i32, height : i32) -> Tabbed {
        Tabbed {
            tabs: Vec::new(),
            active_idx: None,
            width: width,
            height: height,
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_server_tab(&mut self, serv_name : &str) -> Option<usize> {
        match self.find_serv_tab_idx(&serv_name) {
            None => {
                self.tabs.push(Tab {
                    widget: MessagingUI::new(self.width, self.height - 1),
                    src: MsgSource::Serv { serv_name: serv_name.to_owned() },
                });
                self.active_idx = Some(self.tabs.len() - 1);
                Some(self.tabs.len() - 1)
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_chan_tab(&mut self, serv_name : &str, chan_name : &str) -> Option<usize> {
        match self.find_chan_tab_idx(&serv_name, &chan_name) {
            None => {
                match self.find_last_serv_tab_idx(&serv_name) {
                    None => {
                        self.new_server_tab(serv_name);
                        self.new_chan_tab(serv_name, chan_name)
                    },
                    Some(tab_idx) => {
                        self.tabs.insert(tab_idx + 1, Tab {
                            widget: MessagingUI::new(self.width, self.height - 1),
                            src: MsgSource::Chan { serv_name: serv_name.to_owned(),
                                                   chan_name: chan_name.to_owned() },
                        });
                        self.active_idx = Some(tab_idx + 1);
                        Some(tab_idx + 1)
                    }
                }
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    /// Returns index of the new tab if a new tab is created.
    pub fn new_user_tab(&mut self, serv_name : &str, nick : &str) -> Option<usize> {
        match self.find_user_tab_idx(serv_name, nick) {
            None => {
                match self.find_last_serv_tab_idx(&serv_name) {
                    None => {
                        self.new_server_tab(serv_name);
                        self.new_user_tab(serv_name, nick)
                    },
                    Some(tab_idx) => {
                        self.tabs.insert(tab_idx + 1, Tab {
                            widget: MessagingUI::new(self.width, self.height - 1),
                            src: MsgSource::User { serv_name: serv_name.to_owned(),
                                                   nick: nick.to_owned() },
                        });
                        self.active_idx = Some(tab_idx + 1);
                        Some(tab_idx + 1)
                    }
                }
            },
            Some(tab_idx) => {
                self.active_idx = Some(tab_idx);
                None
            }
        }
    }

    pub fn draw(&self, rustbox : &RustBox, pos_x : i32, pos_y : i32) {
        match self.active_idx {
            None => {},
            Some(idx) => self.tabs[idx as usize].widget.draw(rustbox, pos_x, pos_y),
        }

        let mut tab_name_col = pos_x;
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            if self.active_idx == Some(tab_idx) {
                termbox::print(tab_name_col, pos_y + self.height - 1,
                               style::TAB_ACTIVE.fg, style::TAB_ACTIVE.bg,
                               tab.visible_name());
            } else {
                termbox::print(tab_name_col, pos_y + self.height - 1,
                               style::TAB_PASSIVE.fg, style::TAB_PASSIVE.bg,
                               tab.visible_name());
            }
            // len() is OK since sever, chan and nick names are ascii
            tab_name_col += tab.visible_name().len() as i32;
        }
    }

    pub fn keypressed(&mut self, key : Key) -> TabbedRet {
        if key == Key::Tab {
            if self.tabs.len() > 0 {
                self.active_idx = Some((self.active_idx.unwrap() + 1) % self.tabs.len());
            }
            return TabbedRet::KeyHandled;
        }

        match self.active_idx {
            None => TabbedRet::KeyIgnored,
            Some(idx) => {
                match self.tabs[idx as usize].widget.keypressed(key) {
                    WidgetRet::KeyHandled => TabbedRet::KeyHandled,
                    WidgetRet::KeyIgnored => TabbedRet::KeyIgnored,
                    WidgetRet::Input(input) => {
                        TabbedRet::Input {
                            msg: input,
                            from: &self.tabs[idx as usize].src
                        }
                    },
                }
            }
        }
    }

    pub fn resize(&mut self, width : i32, height : i32) {
        self.width = width;
        self.height = height;
        for tab in self.tabs.iter_mut() {
            tab.widget.resize(width, height - 1);
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    // Interfacing with tabs

    fn apply_to_target<F>(&mut self, target : &MsgTarget, f : &F)
            where F : Fn(&mut Tab) -> () {

        // Creating a vector just to make borrow checker happy. Borrow checker
        // sucks once more. Here it sucks 2x, I can't even create a Vec<&mut Tab>,
        // I need a Vec<usize>.
        //
        // (I could use an array on stack but whatever)
        let mut target_idxs : Vec<usize> = Vec::with_capacity(1);

        match target {
            &MsgTarget::Server { serv_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::Serv { serv_name: ref serv_name_ } => {
                            if serv_name == serv_name_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::Chan { serv_name, chan_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::Chan { serv_name: ref serv_name_, chan_name: ref chan_name_ } => {
                            if serv_name == serv_name_ && chan_name == chan_name_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::User { serv_name, nick } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    match &tab.src {
                        &MsgSource::User { serv_name: ref serv_name_, nick: ref nick_ } => {
                            if serv_name == serv_name_ && nick == nick_ {
                                target_idxs.push(tab_idx);
                                break;
                            }
                        },
                        _ => {}
                    }
                }
            },

            &MsgTarget::AllServTabs { serv_name } => {
                for (tab_idx, tab) in self.tabs.iter().enumerate() {
                    if tab.src.serv_name() == serv_name {
                        target_idxs.push(tab_idx);
                    }
                }
            },

            &MsgTarget::AllTabs => {
                for tab_idx in 0 .. self.tabs.len() {
                    target_idxs.push(tab_idx);
                }
            },

            &MsgTarget::CurrentTab => {
                target_idxs.push(self.active_idx.unwrap());
            },

            &MsgTarget::MultipleTabs(ref targets) => {
                for target in targets.iter() {
                    self.apply_to_target(target, f);
                }
            }
        }

        // Create server/chan/user tab when necessary
        if target_idxs.len() == 0 {
            for idx in self.maybe_create_tab(target) {
                target_idxs.push(idx);
            }
        }

        for tab_idx in target_idxs {
            f(unsafe { self.tabs.get_unchecked_mut(tab_idx) });
        }
    }

    fn maybe_create_tab(&mut self, target : &MsgTarget) -> Vec<usize> {
        match target {
            &MsgTarget::Server { serv_name } => {
                opt_to_vec(self.new_server_tab(serv_name))
            },

            &MsgTarget::Chan { serv_name, chan_name } => {
                opt_to_vec(self.new_chan_tab(serv_name, chan_name))
            },

            &MsgTarget::User { serv_name, nick } => {
                opt_to_vec(self.new_user_tab(serv_name, nick))
            },

            &MsgTarget::MultipleTabs(ref targets) => {
                targets.iter().flat_map(|t : &Box<MsgTarget>| self.maybe_create_tab(&*t)).collect()
            }

            _ => vec![]
        }
    }

    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_client_err_msg(msg);
        });
    }

    #[inline]
    pub fn add_client_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_client_msg(msg);
        });
    }

    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_privmsg(sender, msg, tm);
        });
    }

    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_msg(msg, tm);
        });
    }

    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.add_err_msg(msg, tm);
        });
    }

    #[inline]
    pub fn set_topic(&mut self, title : &str, target : &MsgTarget) {
        self.apply_to_target(target, &|tab : &mut Tab| {
            tab.widget.set_topic(title.to_owned());
        });
    }

    ////////////////////////////////////////////////////////////////////////////
    // Helpers

    fn find_serv_tab_idx(&self, serv_name_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::Serv { ref serv_name } => {
                    if serv_name_ == serv_name {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    fn find_chan_tab_idx(&self, serv_name_ : &str, chan_name_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::Chan { ref serv_name, ref chan_name } => {
                    if serv_name_ == serv_name && chan_name_ == chan_name {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    fn find_user_tab_idx(&self, serv_name_ : &str, nick_ : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate() {
            match &tab.src {
                &MsgSource::User { ref serv_name, ref nick } => {
                    if serv_name_ == serv_name && nick_ == nick {
                        return Some(tab_idx);
                    }
                },
                _ => {},
            }
        }
        None
    }

    /// Index of the last tab with the given server name.
    fn find_last_serv_tab_idx(&self, serv_name : &str) -> Option<usize> {
        for (tab_idx, tab) in self.tabs.iter().enumerate().rev() {
            if tab.src.serv_name() == serv_name {
                return Some(tab_idx);
            }
        }
        None
    }
}
<|endoftext|>