<commit_before>use std::env;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};

pub fn target_tests() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop(); // chop off exe name
    path.pop(); // chop off 'deps'
    path.pop(); // chop off 'debug'
    path.push("tests");
    fs::create_dir_all(&path).unwrap();
    path
}

pub fn next_root() -> PathBuf {
    static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
    std::thread_local! {
        static TEST_ID: usize = NEXT_ID.fetch_add(1, SeqCst);
    }
    let id = TEST_ID.with(|n| *n);
    target_tests().join(&format!("t{}", id))
}

pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, next_root(), None)
}

pub fn project_with_params(name: &str, root: PathBuf, fuzz_dir: PathBuf) -> ProjectBuilder {
    ProjectBuilder::new(name, root, Some(fuzz_dir))
}

pub struct Project {
    name: String,
    root: PathBuf,
    fuzz_dir: PathBuf,
}

pub struct ProjectBuilder {
    project: Project,
    saw_manifest: bool,
    saw_main_or_lib: bool,
}

impl ProjectBuilder {
    pub fn new(name: &str, root: PathBuf, fuzz_dir_opt: Option<PathBuf>) -> ProjectBuilder {
        println!(" ============ {} =============== ", root.display());
        drop(fs::remove_dir_all(&root));
        fs::create_dir_all(&root).unwrap();
        let fuzz_dir = fuzz_dir_opt.unwrap_or_else(|| root.join("fuzz"));
        ProjectBuilder {
            project: Project {
                name: name.to_string(),
                root,
                fuzz_dir,
            },
            saw_manifest: false,
            saw_main_or_lib: false,
        }
    }

    pub fn root(&self) -> PathBuf {
        self.project.root()
    }

    pub fn with_fuzz(&mut self) -> &mut Self {
        self.file(
            self.project.fuzz_dir.join("Cargo.toml"),
            &format!(
                r#"
                    [package]
                    name = "{name}-fuzz"
                    version = "0.0.0"
                    publish = false
                    edition = "2021"

                    [package.metadata]
                    cargo-fuzz = true

                    [workspace]
                    members = ["."]

                    [dependencies]
                    libfuzzer-sys = "0.4"

                    [dependencies.{name}]
                    path = ".."
                "#,
                name = self.project.name,
            ),
        )
    }

    pub fn fuzz_target(&mut self, name: &str, body: &str) -> &mut Self {
        let path = self.project.fuzz_target_path(name);

        let mut fuzz_cargo_toml = fs::OpenOptions::new()
            .write(true)
            .append(true)
            .open(self.project.fuzz_dir.join("Cargo.toml"))
            .unwrap();
        write!(
            &mut fuzz_cargo_toml,
            r#"
                [[bin]]
                name = "{name}"
                path = "{path}"
                test = false
                doc = false
            "#,
            name = name,
            path = path.display(),
        )
        .unwrap();

        self.file(path, body)
    }

    pub fn file<B: AsRef<Path>>(&mut self, path: B, body: &str) -> &mut Self {
        self._file(path.as_ref(), body);
        self
    }

    fn _file(&mut self, path: &Path, body: &str) {
        if path == Path::new("Cargo.toml") {
            self.saw_manifest = true;
        }
        if path == Path::new("src").join("lib.rs") || path == Path::new("src").join("main.rs") {
            self.saw_main_or_lib = true;
        }
        let path = self.root().join(path);
        fs::create_dir_all(path.parent().unwrap()).unwrap();
        fs::write(self.root().join(path), body).unwrap();
    }

    pub fn default_cargo_toml(&mut self) -> &mut Self {
        self.file(
            "Cargo.toml",
            &format!(
                r#"
                    [package]
                    name = "{name}"
                    version = "1.0.0"
                "#,
                name = self.project.name,
            ),
        )
    }

    pub fn default_src_lib(&mut self) -> &mut Self {
        self.file(
            Path::new("src").join("lib.rs"),
            r#"
                pub fn pass_fuzzing(data: &[u8]) {
                    let _ = data;
                }

                pub fn fail_fuzzing(data: &[u8]) {
                    if data.len() == 7 {
                        panic!("I'm afraid of number 7");
                    }
                }
            "#,
        )
    }

    pub fn build(&mut self) -> Project {
        if !self.saw_manifest {
            self.default_cargo_toml();
        }
        if !self.saw_main_or_lib {
            self.default_src_lib();
        }
        Project {
            fuzz_dir: self.project.fuzz_dir.clone(),
            name: self.project.name.clone(),
            root: self.project.root.clone(),
        }
    }
}

impl Project {
    pub fn root(&self) -> PathBuf {
        self.root.clone()
    }

    /// Get the build directory for the fuzz targets.
    ///
    /// This will panic if no fuzz targets have been built yet.
    pub fn fuzz_build_dir(&self) -> PathBuf {
        // Because we pass an explicit `--target` to builds, its as if we were
        // cross-compiling even when we technically aren't, and the artifacts
        // end up in `target/<triple>/*`.
        target_tests()
            .join("target")
            .read_dir()
            .expect("should get directory entries for tests' target directory")
            .map(|e| {
                e.expect("should read an entry from the tests' target directory OK")
                    .path()

            }).find(|d| d.is_dir() && !d.ends_with("debug") && !d.ends_with("release"))
            .unwrap()
    }

    pub fn fuzz_dir(&self) -> &Path {
        &self.fuzz_dir
    }

    pub fn fuzz_cargo_toml(&self) -> PathBuf {
        self.fuzz_dir.join("Cargo.toml")
    }

    pub fn fuzz_targets_dir(&self) -> PathBuf {
        self.fuzz_dir.join("fuzz_targets")
    }

    pub fn fuzz_target_path(&self, target: &str) -> PathBuf {
        let mut p = self.fuzz_targets_dir().join(target);
        p.set_extension("rs");
        p
    }

    pub fn fuzz_coverage_dir(&self, target: &str) -> PathBuf {
        self.fuzz_dir.join("coverage").join(target)
    }

    pub fn cargo_fuzz(&self) -> Command {
        let mut cmd = super::cargo_fuzz();
        cmd.current_dir(&self.root)
            // Even though this disables some parallelism, we won't need to
            // download and compile libbfuzzer-sys multiple times.
            .env("CARGO_HOME", target_tests().join("cargo-home"))
            .env("CARGO_TARGET_DIR", target_tests().join("target"));
        cmd
    }
}
<commit_msg>whitespace<commit_after>use std::env;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};

pub fn target_tests() -> PathBuf {
    let mut path = env::current_exe().unwrap();
    path.pop(); // chop off exe name
    path.pop(); // chop off 'deps'
    path.pop(); // chop off 'debug'
    path.push("tests");
    fs::create_dir_all(&path).unwrap();
    path
}

pub fn next_root() -> PathBuf {
    static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
    std::thread_local! {
        static TEST_ID: usize = NEXT_ID.fetch_add(1, SeqCst);
    }
    let id = TEST_ID.with(|n| *n);
    target_tests().join(&format!("t{}", id))
}

pub fn project(name: &str) -> ProjectBuilder {
    ProjectBuilder::new(name, next_root(), None)
}

pub fn project_with_params(name: &str, root: PathBuf, fuzz_dir: PathBuf) -> ProjectBuilder {
    ProjectBuilder::new(name, root, Some(fuzz_dir))
}

pub struct Project {
    name: String,
    root: PathBuf,
    fuzz_dir: PathBuf,
}

pub struct ProjectBuilder {
    project: Project,
    saw_manifest: bool,
    saw_main_or_lib: bool,
}

impl ProjectBuilder {
    pub fn new(name: &str, root: PathBuf, fuzz_dir_opt: Option<PathBuf>) -> ProjectBuilder {
        println!(" ============ {} =============== ", root.display());
        drop(fs::remove_dir_all(&root));
        fs::create_dir_all(&root).unwrap();
        let fuzz_dir = fuzz_dir_opt.unwrap_or_else(|| root.join("fuzz"));
        ProjectBuilder {
            project: Project {
                name: name.to_string(),
                root,
                fuzz_dir,
            },
            saw_manifest: false,
            saw_main_or_lib: false,
        }
    }

    pub fn root(&self) -> PathBuf {
        self.project.root()
    }

    pub fn with_fuzz(&mut self) -> &mut Self {
        self.file(
            self.project.fuzz_dir.join("Cargo.toml"),
            &format!(
                r#"
                    [package]
                    name = "{name}-fuzz"
                    version = "0.0.0"
                    publish = false
                    edition = "2021"

                    [package.metadata]
                    cargo-fuzz = true

                    [workspace]
                    members = ["."]

                    [dependencies]
                    libfuzzer-sys = "0.4"

                    [dependencies.{name}]
                    path = ".."
                "#,
                name = self.project.name,
            ),
        )
    }

    pub fn fuzz_target(&mut self, name: &str, body: &str) -> &mut Self {
        let path = self.project.fuzz_target_path(name);

        let mut fuzz_cargo_toml = fs::OpenOptions::new()
            .write(true)
            .append(true)
            .open(self.project.fuzz_dir.join("Cargo.toml"))
            .unwrap();
        write!(
            &mut fuzz_cargo_toml,
            r#"
                [[bin]]
                name = "{name}"
                path = "{path}"
                test = false
                doc = false
            "#,
            name = name,
            path = path.display(),
        )
        .unwrap();

        self.file(path, body)
    }

    pub fn file<B: AsRef<Path>>(&mut self, path: B, body: &str) -> &mut Self {
        self._file(path.as_ref(), body);
        self
    }

    fn _file(&mut self, path: &Path, body: &str) {
        if path == Path::new("Cargo.toml") {
            self.saw_manifest = true;
        }
        if path == Path::new("src").join("lib.rs") || path == Path::new("src").join("main.rs") {
            self.saw_main_or_lib = true;
        }
        let path = self.root().join(path);
        fs::create_dir_all(path.parent().unwrap()).unwrap();
        fs::write(self.root().join(path), body).unwrap();
    }

    pub fn default_cargo_toml(&mut self) -> &mut Self {
        self.file(
            "Cargo.toml",
            &format!(
                r#"
                    [package]
                    name = "{name}"
                    version = "1.0.0"
                "#,
                name = self.project.name,
            ),
        )
    }

    pub fn default_src_lib(&mut self) -> &mut Self {
        self.file(
            Path::new("src").join("lib.rs"),
            r#"
                pub fn pass_fuzzing(data: &[u8]) {
                    let _ = data;
                }

                pub fn fail_fuzzing(data: &[u8]) {
                    if data.len() == 7 {
                        panic!("I'm afraid of number 7");
                    }
                }
            "#,
        )
    }

    pub fn build(&mut self) -> Project {
        if !self.saw_manifest {
            self.default_cargo_toml();
        }
        if !self.saw_main_or_lib {
            self.default_src_lib();
        }
        Project {
            fuzz_dir: self.project.fuzz_dir.clone(),
            name: self.project.name.clone(),
            root: self.project.root.clone(),
        }
    }
}

impl Project {
    pub fn root(&self) -> PathBuf {
        self.root.clone()
    }

    /// Get the build directory for the fuzz targets.
    ///
    /// This will panic if no fuzz targets have been built yet.
    pub fn fuzz_build_dir(&self) -> PathBuf {
        // Because we pass an explicit `--target` to builds, its as if we were
        // cross-compiling even when we technically aren't, and the artifacts
        // end up in `target/<triple>/*`.
        target_tests()
            .join("target")
            .read_dir()
            .expect("should get directory entries for tests' target directory")
            .map(|e| {
                e.expect("should read an entry from the tests' target directory OK")
                    .path()
            }).find(|d| d.is_dir() && !d.ends_with("debug") && !d.ends_with("release"))
            .unwrap()
    }

    pub fn fuzz_dir(&self) -> &Path {
        &self.fuzz_dir
    }

    pub fn fuzz_cargo_toml(&self) -> PathBuf {
        self.fuzz_dir.join("Cargo.toml")
    }

    pub fn fuzz_targets_dir(&self) -> PathBuf {
        self.fuzz_dir.join("fuzz_targets")
    }

    pub fn fuzz_target_path(&self, target: &str) -> PathBuf {
        let mut p = self.fuzz_targets_dir().join(target);
        p.set_extension("rs");
        p
    }

    pub fn fuzz_coverage_dir(&self, target: &str) -> PathBuf {
        self.fuzz_dir.join("coverage").join(target)
    }

    pub fn cargo_fuzz(&self) -> Command {
        let mut cmd = super::cargo_fuzz();
        cmd.current_dir(&self.root)
            // Even though this disables some parallelism, we won't need to
            // download and compile libbfuzzer-sys multiple times.
            .env("CARGO_HOME", target_tests().join("cargo-home"))
            .env("CARGO_TARGET_DIR", target_tests().join("target"));
        cmd
    }
}
<|endoftext|>


<commit_before>

#![allow(single_match, unused_assignments, unused_variables)]

fn test1() {
    let mut x = 0;
    loop { // never_loop
        x += 1;
        if x == 1 {
            return
        }
        break;
    }
}

fn test2() {
    let mut x = 0;
    loop {
        x += 1;
        if x == 1 {
            break
        }
    }
}

fn test3() {
    let mut x = 0;
    loop { // never loops
        x += 1;
        break
    }
}

fn test4() {
    let mut x = 1;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => (),
        }
    }
}

fn test5() {
    let i = 0;
	loop { // never loops
        while i == 0 { // never loops
            break
        }
        return
	}
}

fn test6() {
    let mut x = 0;
    'outer: loop {
        x += 1;
		loop { // never loops
            if x == 5 { break }
			continue 'outer
		}
		return
	}
}

fn test7() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            1 => continue,
            _ => (),
        }
        return
    }
}

fn test8() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => continue,
        }
    }
}

fn test9() {
    let x = Some(1);
    while let Some(y) = x { // never loops
        return
    }
}

fn test10() {
    for x in 0..10 { // never loops
        match x {
            1 => break,
            _ => return,
        }
    }
}

fn test11<F: FnMut() -> i32>(mut f: F) {
    loop {
        return match f() {
            1 => continue,
            _ => (),
        }
    }
}

pub fn test12(a: bool, b: bool) {
    'label: loop {
        loop {
            if a {
                continue 'label;
            }
            if b {
                break;
            }
        }
        break;
    }
}

fn main() {
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11(|| 0);
    test12(true, false);
}

<commit_msg>add never_loop test<commit_after>

#![allow(single_match, unused_assignments, unused_variables)]

fn test1() {
    let mut x = 0;
    loop { // never_loop
        x += 1;
        if x == 1 {
            return
        }
        break;
    }
}

fn test2() {
    let mut x = 0;
    loop {
        x += 1;
        if x == 1 {
            break
        }
    }
}

fn test3() {
    let mut x = 0;
    loop { // never loops
        x += 1;
        break
    }
}

fn test4() {
    let mut x = 1;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => (),
        }
    }
}

fn test5() {
    let i = 0;
	loop { // never loops
        while i == 0 { // never loops
            break
        }
        return
	}
}

fn test6() {
    let mut x = 0;
    'outer: loop {
        x += 1;
		loop { // never loops
            if x == 5 { break }
			continue 'outer
		}
		return
	}
}

fn test7() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            1 => continue,
            _ => (),
        }
        return
    }
}

fn test8() {
    let mut x = 0;
    loop {
        x += 1;
        match x {
            5 => return,
            _ => continue,
        }
    }
}

fn test9() {
    let x = Some(1);
    while let Some(y) = x { // never loops
        return
    }
}

fn test10() {
    for x in 0..10 { // never loops
        match x {
            1 => break,
            _ => return,
        }
    }
}

fn test11<F: FnMut() -> i32>(mut f: F) {
    loop {
        return match f() {
            1 => continue,
            _ => (),
        }
    }
}

pub fn test12(a: bool, b: bool) {
    'label: loop {
        loop {
            if a {
                continue 'label;
            }
            if b {
                break;
            }
        }
        break;
    }
}

pub fn test13() {
    let mut a = true;
    loop { // infinite loop
        while a {
            if true {
                a = false;
                continue;
            }
            return;
        }
    }
}

fn main() {
    test1();
    test2();
    test3();
    test4();
    test5();
    test6();
    test7();
    test8();
    test9();
    test10();
    test11(|| 0);
    test12(true, false);
    test13();
}

<|endoftext|>


<commit_before>use crate::actions::Action;
use crate::board::{CellEquivalence, Cells};
use crate::coordinate::Coordinate;
use crate::end;
use crate::Role;
use r4::iterate;

use std::hash::{Hash, Hasher};

#[derive(Debug)]
pub struct State {
  board: Cells,
  active_role: Role,
  proposed_terminate: bool,
  terminate_decision: Option<end::Decision>,
  equivalence_class: &'static dyn CellEquivalence,
}

impl State {
  pub fn new(board: Cells, equivalence_class: &'static CellEquivalence) -> Self {
    State {
      board: board,
      active_role: Role::Dwarf,
      proposed_terminate: false,
      terminate_decision: None,
      equivalence_class: equivalence_class,
    }
  }

  pub fn cells(&self) -> &Cells {
    &self.board
  }

  pub fn active_role(&self) -> &Role {
    &self.active_role
  }

  pub fn actions<'s>(&'s self) -> impl Iterator<Item = Action> + 's {
    self.role_actions(*self.active_role())
  }

  pub fn role_actions<'s>(&'s self, r: Role) -> impl Iterator<Item = Action> + 's {
    let must_handle_end_proposal = self.proposed_terminate && self.terminate_decision.is_none();
    let handle_end_proposal = iterate![if must_handle_end_proposal;
                     yield Action::HandleEndProposal(end::Decision::Accept);
                     yield Action::HandleEndProposal(end::Decision::Decline)];
    let regular_moves = iterate![if !must_handle_end_proposal;
                     for action in self.board.role_actions(r, self.terminate_decision.is_none());
                     yield action];
    handle_end_proposal.chain(regular_moves)
  }

  pub fn position_actions<'s>(&'s self, position: Coordinate) -> impl Iterator<Item = Action> + 's {
    self.board.position_actions(position)
  }

  pub fn toggle_active_role(&mut self) {
    self.active_role = self.active_role.toggle()
  }

  // pub fn set_from_convolved(&mut self, convolved: &Self) {
  //   use super::board::CellEquivalence;

  //   let mut convolutions = Convolution::all().iter();
  //   loop {
  //     match convolutions.next() {
  //       None => panic!("no match"),
  //       Some(v) => {
  //         let mut scratch_cells = Cells::new();
  //         for (coordinate, content) in convolved.board.cells_iter() {
  //           scratch_cells[v.convolve(coordinate)] = content;
  //         }
  //         if board::SimpleEquivalence::boards_equal(&convolved.board, &scratch_cells) {
  //           for (coordinate, content) in convolved.board.cells_iter() {
  //             self.board[v.inverse(coordinate)] = content;
  //           }
  //           break;
  //         }
  //       }
  //     }
  //   }
  //   self.active_role = convolved.active_role;
  //   self.proposed_terminate = convolved.proposed_terminate;
  //   self.terminate_decision = convolved.terminate_decision;
  // }

  pub fn do_action(&mut self, a: &Action) {
    match a {
      &Action::ProposeEnd => self.proposed_terminate = true,
      &Action::HandleEndProposal(d) => self.terminate_decision = Some(d),
      _ => {
        self.proposed_terminate = false;
        self.terminate_decision = None;
        self.board.do_action(a)
      }
    }
    self.toggle_active_role();
  }

  pub fn terminated(&self) -> bool {
    self.terminate_decision == Some(end::Decision::Accept)
      || self.board.role_actions(Role::Dwarf, false).count() == 0
      || self.board.role_actions(Role::Troll, false).count() == 0
  }

  pub fn board(&self) -> &Cells {
    &self.board
  }

  pub fn opponent_proposed_end(&self) -> bool {
    self.proposed_terminate
  }
}

impl Clone for State {
  fn clone(&self) -> Self {
    State {
      board: self.board.clone(),
      active_role: self.active_role,
      proposed_terminate: self.proposed_terminate,
      terminate_decision: self.terminate_decision,
      equivalence_class: self.equivalence_class,
    }
  }
}

impl Hash for State {
  fn hash<H: Hasher>(&self, state: &mut H) {
    (self.equivalence_class.hash_board(&self.board)).hash(state);
    self.active_role.hash(state);
    self.proposed_terminate.hash(state);
    self.terminate_decision.hash(state);
  }
}

impl PartialEq for State {
  fn eq(&self, other: &Self) -> bool {
    self.equivalence_class.boards_equal(&self.board, &other.board) &&
      self.active_role == other.active_role &&
      self.proposed_terminate == other.proposed_terminate &&
      self.terminate_decision == other.terminate_decision
  }
}

impl Eq for State {}

#[cfg(test)]
mod test {
  use super::*;
  use crate::actions::Action;
  use crate::board::{self, Cells};
  use crate::end;
  use std::collections::HashMap;

  fn new_simple_state() -> State {
    State::new(Cells::default(), &board::SIMPLE_EQUIVALENCE)
  }

  fn new_untransposing_state() -> State {
    State::new(Cells::default(), &board::TRANSPOSITIONAL_EQUIVALENCE)
  }

  #[test]
  fn null_move_equivalence() {
    assert!(new_simple_state() == new_simple_state());
    assert!(new_untransposing_state() == new_untransposing_state());
  }

  #[test]
  fn simple_null_move_hash_collision() {
    let mut table = HashMap::new();
    let s1 = new_simple_state();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&new_simple_state()));
  }

  #[test]
  fn transposed_null_move_hash_collision() {
    let mut table = HashMap::new();
    let s1 = new_untransposing_state();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&new_untransposing_state()));
  }

  #[test]
  fn simple_move_equivalence() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((2, 3), (10, 3)));
    assert!(s1 == s2);
  }

  fn check_hash_collision(s1: State, s2: State) {
    let mut table = HashMap::new();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&s2));
  }

  #[test]
  fn simple_move_hash_collision() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((2, 3), (10, 3)));
    check_hash_collision(s1, s2);
  }

  #[test]
  fn transposed_move_equivalence() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((5, 0), (1, 5)));
    assert!(s1 == s2);

    s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (5, 1)));
    assert!(s1 == s2);
  }

  #[test]
  fn transposed_move_hash_collision() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((5, 0), (1, 5)));
    check_hash_collision(s1, s2);

    s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (5, 1)));
    check_hash_collision(s1, s2);
  }

  #[test]
  fn simple_move_nonequivalence_1() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((14, 9), (6, 1)));
    assert!(s1 != s2);
  }

  #[test]
  fn simple_move_nonequivalence_2() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((0, 5), (8, 5)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((0, 5), (9, 5)));
    assert!(s1 != s2);
  }

  #[test]
  fn transposed_move_nonequivalence_1() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((14, 9), (6, 1)));
    assert!(s1 != s2);
  }

  #[test]
  fn transposed_move_nonequivalence_2() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((0, 5), (8, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (9, 5)));
    assert!(s1 != s2);
  }

  #[test]
  fn propose_end_ok() {
    let state = new_simple_state();
    assert!(!state.proposed_terminate);
    assert_eq!(state.terminate_decision, None);
    let available_actions: Vec<Action> = state.actions().collect();
    assert!(available_actions.contains(&Action::ProposeEnd));
  }

  #[test]
  fn no_propose_end_ok() {
    let mut state = new_simple_state();
    assert!(!state.proposed_terminate);
    state.do_action(&Action::ProposeEnd);
    assert!(state.proposed_terminate);
    let available_actions: Vec<Action> = state.actions().collect();
    assert_eq!(
      available_actions,
      vec!(
        Action::HandleEndProposal(end::Decision::Accept),
        Action::HandleEndProposal(end::Decision::Decline)
      )
    );
    assert!(!state.terminated());
  }

  #[test]
  fn accept_terminate_ok() {
    let mut state = new_simple_state();
    state.do_action(&Action::ProposeEnd);
    state.do_action(&Action::HandleEndProposal(end::Decision::Accept));
    assert!(state.terminated());
  }

  #[test]
  fn decline_terminate_ok() {
    let mut state = new_simple_state();
    state.do_action(&Action::ProposeEnd);
    state.do_action(&Action::HandleEndProposal(end::Decision::Decline));
    assert!(!state.terminated());
    let mut available_actions: Vec<Action> = state.actions().collect();
    assert!(!available_actions.contains(&Action::ProposeEnd));

    state.do_action(&available_actions[0]);
    available_actions = state.actions().collect();
    assert!(available_actions.contains(&Action::ProposeEnd));
  }
}
<commit_msg>Add a function to basic game state for computing per-player score.<commit_after>use crate::actions::Action;
use crate::board::{CellEquivalence, Cells};
use crate::coordinate::Coordinate;
use crate::end;
use crate::Role;
use r4::iterate;

use std::hash::{Hash, Hasher};

#[derive(Debug)]
pub struct State {
  board: Cells,
  active_role: Role,
  proposed_terminate: bool,
  terminate_decision: Option<end::Decision>,
  equivalence_class: &'static dyn CellEquivalence,
}

impl State {
  pub fn new(board: Cells, equivalence_class: &'static CellEquivalence) -> Self {
    State {
      board: board,
      active_role: Role::Dwarf,
      proposed_terminate: false,
      terminate_decision: None,
      equivalence_class: equivalence_class,
    }
  }

  pub fn cells(&self) -> &Cells {
    &self.board
  }

  pub fn active_role(&self) -> &Role {
    &self.active_role
  }

  pub fn actions<'s>(&'s self) -> impl Iterator<Item = Action> + 's {
    self.role_actions(*self.active_role())
  }

  pub fn role_actions<'s>(&'s self, r: Role) -> impl Iterator<Item = Action> + 's {
    let must_handle_end_proposal = self.proposed_terminate && self.terminate_decision.is_none();
    let handle_end_proposal = iterate![if must_handle_end_proposal;
                     yield Action::HandleEndProposal(end::Decision::Accept);
                     yield Action::HandleEndProposal(end::Decision::Decline)];
    let regular_moves = iterate![if !must_handle_end_proposal;
                     for action in self.board.role_actions(r, self.terminate_decision.is_none());
                     yield action];
    handle_end_proposal.chain(regular_moves)
  }

  pub fn position_actions<'s>(&'s self, position: Coordinate) -> impl Iterator<Item = Action> + 's {
    self.board.position_actions(position)
  }

  pub fn toggle_active_role(&mut self) {
    self.active_role = self.active_role.toggle()
  }

  // pub fn set_from_convolved(&mut self, convolved: &Self) {
  //   use super::board::CellEquivalence;

  //   let mut convolutions = Convolution::all().iter();
  //   loop {
  //     match convolutions.next() {
  //       None => panic!("no match"),
  //       Some(v) => {
  //         let mut scratch_cells = Cells::new();
  //         for (coordinate, content) in convolved.board.cells_iter() {
  //           scratch_cells[v.convolve(coordinate)] = content;
  //         }
  //         if board::SimpleEquivalence::boards_equal(&convolved.board, &scratch_cells) {
  //           for (coordinate, content) in convolved.board.cells_iter() {
  //             self.board[v.inverse(coordinate)] = content;
  //           }
  //           break;
  //         }
  //       }
  //     }
  //   }
  //   self.active_role = convolved.active_role;
  //   self.proposed_terminate = convolved.proposed_terminate;
  //   self.terminate_decision = convolved.terminate_decision;
  // }

  pub fn do_action(&mut self, a: &Action) {
    match a {
      &Action::ProposeEnd => self.proposed_terminate = true,
      &Action::HandleEndProposal(d) => self.terminate_decision = Some(d),
      _ => {
        self.proposed_terminate = false;
        self.terminate_decision = None;
        self.board.do_action(a)
      }
    }
    self.toggle_active_role();
  }

  pub fn terminated(&self) -> bool {
    self.terminate_decision == Some(end::Decision::Accept)
      || self.board.role_actions(Role::Dwarf, false).count() == 0
      || self.board.role_actions(Role::Troll, false).count() == 0
  }

  pub fn board(&self) -> &Cells {
    &self.board
  }

  pub fn opponent_proposed_end(&self) -> bool {
    self.proposed_terminate
  }

  pub fn score(&self, role: Role) -> u16 {
    let multiplier = match role {
      Role::Dwarf => 1u16,
      Role::Troll => 4u16,
    };
    self.board.occupied_iter(role).fold(0u16, |acc, _| acc + multiplier)
  }
}

impl Clone for State {
  fn clone(&self) -> Self {
    State {
      board: self.board.clone(),
      active_role: self.active_role,
      proposed_terminate: self.proposed_terminate,
      terminate_decision: self.terminate_decision,
      equivalence_class: self.equivalence_class,
    }
  }
}

impl Hash for State {
  fn hash<H: Hasher>(&self, state: &mut H) {
    (self.equivalence_class.hash_board(&self.board)).hash(state);
    self.active_role.hash(state);
    self.proposed_terminate.hash(state);
    self.terminate_decision.hash(state);
  }
}

impl PartialEq for State {
  fn eq(&self, other: &Self) -> bool {
    self.equivalence_class.boards_equal(&self.board, &other.board) &&
      self.active_role == other.active_role &&
      self.proposed_terminate == other.proposed_terminate &&
      self.terminate_decision == other.terminate_decision
  }
}

impl Eq for State {}

#[cfg(test)]
mod test {
  use super::*;
  use crate::actions::Action;
  use crate::board::{self, Cells};
  use crate::end;
  use std::collections::HashMap;

  fn new_simple_state() -> State {
    State::new(Cells::default(), &board::SIMPLE_EQUIVALENCE)
  }

  fn new_untransposing_state() -> State {
    State::new(Cells::default(), &board::TRANSPOSITIONAL_EQUIVALENCE)
  }

  #[test]
  fn null_move_equivalence() {
    assert!(new_simple_state() == new_simple_state());
    assert!(new_untransposing_state() == new_untransposing_state());
  }

  #[test]
  fn simple_null_move_hash_collision() {
    let mut table = HashMap::new();
    let s1 = new_simple_state();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&new_simple_state()));
  }

  #[test]
  fn transposed_null_move_hash_collision() {
    let mut table = HashMap::new();
    let s1 = new_untransposing_state();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&new_untransposing_state()));
  }

  #[test]
  fn simple_move_equivalence() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((2, 3), (10, 3)));
    assert!(s1 == s2);
  }

  fn check_hash_collision(s1: State, s2: State) {
    let mut table = HashMap::new();
    assert!(table.is_empty());
    table.insert(s1, true);
    assert_eq!(1, table.len());
    assert!(table.contains_key(&s2));
  }

  #[test]
  fn simple_move_hash_collision() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((2, 3), (10, 3)));
    check_hash_collision(s1, s2);
  }

  #[test]
  fn transposed_move_equivalence() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((5, 0), (1, 5)));
    assert!(s1 == s2);

    s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (5, 1)));
    assert!(s1 == s2);
  }

  #[test]
  fn transposed_move_hash_collision() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((5, 0), (1, 5)));
    check_hash_collision(s1, s2);

    s1 = new_untransposing_state();
    s1.do_action(&move_literal!((5, 0), (1, 5)));
    s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (5, 1)));
    check_hash_collision(s1, s2);
  }

  #[test]
  fn simple_move_nonequivalence_1() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((14, 9), (6, 1)));
    assert!(s1 != s2);
  }

  #[test]
  fn simple_move_nonequivalence_2() {
    let mut s1 = new_simple_state();
    s1.do_action(&move_literal!((0, 5), (8, 5)));
    let mut s2 = new_simple_state();
    s2.do_action(&move_literal!((0, 5), (9, 5)));
    assert!(s1 != s2);
  }

  #[test]
  fn transposed_move_nonequivalence_1() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((2, 3), (10, 3)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((14, 9), (6, 1)));
    assert!(s1 != s2);
  }

  #[test]
  fn transposed_move_nonequivalence_2() {
    let mut s1 = new_untransposing_state();
    s1.do_action(&move_literal!((0, 5), (8, 5)));
    let mut s2 = new_untransposing_state();
    s2.do_action(&move_literal!((0, 5), (9, 5)));
    assert!(s1 != s2);
  }

  #[test]
  fn propose_end_ok() {
    let state = new_simple_state();
    assert!(!state.proposed_terminate);
    assert_eq!(state.terminate_decision, None);
    let available_actions: Vec<Action> = state.actions().collect();
    assert!(available_actions.contains(&Action::ProposeEnd));
  }

  #[test]
  fn no_propose_end_ok() {
    let mut state = new_simple_state();
    assert!(!state.proposed_terminate);
    state.do_action(&Action::ProposeEnd);
    assert!(state.proposed_terminate);
    let available_actions: Vec<Action> = state.actions().collect();
    assert_eq!(
      available_actions,
      vec!(
        Action::HandleEndProposal(end::Decision::Accept),
        Action::HandleEndProposal(end::Decision::Decline)
      )
    );
    assert!(!state.terminated());
  }

  #[test]
  fn accept_terminate_ok() {
    let mut state = new_simple_state();
    state.do_action(&Action::ProposeEnd);
    state.do_action(&Action::HandleEndProposal(end::Decision::Accept));
    assert!(state.terminated());
  }

  #[test]
  fn decline_terminate_ok() {
    let mut state = new_simple_state();
    state.do_action(&Action::ProposeEnd);
    state.do_action(&Action::HandleEndProposal(end::Decision::Decline));
    assert!(!state.terminated());
    let mut available_actions: Vec<Action> = state.actions().collect();
    assert!(!available_actions.contains(&Action::ProposeEnd));

    state.do_action(&available_actions[0]);
    available_actions = state.actions().collect();
    assert!(available_actions.contains(&Action::ProposeEnd));
  }

  #[test]
  fn compute_score() {
    let state = new_simple_state();
    assert_eq!(32, state.score(Role::Dwarf));
    assert_eq!(32, state.score(Role::Troll));
  }
}
<|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/. */

use euclid::size::Size2D;
use layers::platform::surface::{NativeDisplay, NativeSurface};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::hash::{Hash, Hasher};

/// This is a struct used to store surfaces when they are not in use.
/// The paint task can quickly query for a particular size of surface when it
/// needs it.
pub struct SurfaceMap {
    /// A HashMap that stores the Buffers.
    map: HashMap<SurfaceKey, SurfaceValue>,
    /// The current amount of memory stored by the SurfaceMap's surfaces.
    mem: usize,
    /// The maximum allowed memory. Unused surfaces will be deleted
    /// when this threshold is exceeded.
    max_mem: usize,
    /// A monotonically increasing counter to track how recently tile sizes were used.
    counter: usize,
}

/// A key with which to store surfaces. It is based on the size of the surface.
#[derive(Eq, Copy, Clone)]
struct SurfaceKey([i32; 2]);

impl Hash for SurfaceKey {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let SurfaceKey(ref bytes) = *self;
        bytes.hash(state);
    }
}

impl PartialEq for SurfaceKey {
    fn eq(&self, other: &SurfaceKey) -> bool {
        let SurfaceKey(s) = *self;
        let SurfaceKey(o) = *other;
        s[0] == o[0] && s[1] == o[1]
    }
}

/// Create a key from a given size
impl SurfaceKey {
    fn get(input: Size2D<i32>) -> SurfaceKey {
        SurfaceKey([input.width, input.height])
    }
}

/// A helper struct to keep track of surfaces in the HashMap
struct SurfaceValue {
    /// An array of surfaces, all the same size
    surfaces: Vec<NativeSurface>,
    /// The counter when this size was last requested
    last_action: usize,
}

impl SurfaceMap {
    // Creates a new SurfaceMap with a given surface limit.
    pub fn new(max_mem: usize) -> SurfaceMap {
        SurfaceMap {
            map: HashMap::new(),
            mem: 0,
            max_mem: max_mem,
            counter: 0,
        }
    }

    pub fn insert_surfaces<I>(&mut self, display: &NativeDisplay, surfaces: I)
        where I: IntoIterator<Item=NativeSurface>
    {
        for surface in surfaces {
            self.insert(display, surface);
        }
    }

    /// Insert a new buffer into the map.
    pub fn insert(&mut self, display: &NativeDisplay, mut new_surface: NativeSurface) {
        let new_key = SurfaceKey::get(new_surface.get_size());

        // If all our surfaces are the same size and we're already at our
        // memory limit, no need to store this new buffer; just let it drop.
        let new_total_memory_usage = self.mem + new_surface.get_memory_usage();
        if new_total_memory_usage > self.max_mem && self.map.len() == 1 &&
            self.map.contains_key(&new_key) {
            new_surface.destroy(display);
            return;
        }

        self.mem = new_total_memory_usage;
        new_surface.mark_wont_leak();

        // use lazy insertion function to prevent unnecessary allocation
        let counter = &self.counter;
        match self.map.entry(new_key) {
            Occupied(entry) => {
                entry.into_mut().surfaces.push(new_surface);
            }
            Vacant(entry) => {
                entry.insert(SurfaceValue {
                    surfaces: vec!(new_surface),
                    last_action: *counter,
                });
            }
        }

        let mut opt_key: Option<SurfaceKey> = None;
        while self.mem > self.max_mem {
            let old_key = match opt_key {
                Some(key) => key,
                None => {
                    match self.map.iter().min_by(|&(_, x)| x.last_action) {
                        Some((k, _)) => *k,
                        None => panic!("SurfaceMap: tried to delete with no elements in map"),
                    }
                }
            };
            if {
                let list = &mut self.map.get_mut(&old_key).unwrap().surfaces;
                let mut condemned_surface = list.pop().take().unwrap();
                self.mem -= condemned_surface.get_memory_usage();
                condemned_surface.destroy(display);
                list.is_empty()
            }
            { // then
                self.map.remove(&old_key); // Don't store empty vectors!
                opt_key = None;
            } else {
                opt_key = Some(old_key);
            }
        }
    }

    // Try to find a buffer for the given size.
    pub fn find(&mut self, size: Size2D<i32>) -> Option<NativeSurface> {
        let mut flag = false; // True if key needs to be popped after retrieval.
        let key = SurfaceKey::get(size);
        let ret = match self.map.get_mut(&key) {
            Some(ref mut surface_val) => {
                surface_val.last_action = self.counter;
                self.counter += 1;

                let surface = surface_val.surfaces.pop().take().unwrap();
                self.mem -= surface.get_memory_usage();
                if surface_val.surfaces.is_empty() {
                    flag = true;
                }
                Some(surface)
            }
            None => None,
        };

        if flag {
            self.map.remove(&key); // Don't store empty vectors!
        }

        ret
    }

    pub fn mem(&self) -> usize {
        self.mem
    }
}
<commit_msg>Auto merge of #8923 - saneyuki:warning2, r=nox<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/. */

use euclid::size::Size2D;
use layers::platform::surface::{NativeDisplay, NativeSurface};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::hash::{Hash, Hasher};

/// This is a struct used to store surfaces when they are not in use.
/// The paint task can quickly query for a particular size of surface when it
/// needs it.
pub struct SurfaceMap {
    /// A HashMap that stores the Buffers.
    map: HashMap<SurfaceKey, SurfaceValue>,
    /// The current amount of memory stored by the SurfaceMap's surfaces.
    mem: usize,
    /// The maximum allowed memory. Unused surfaces will be deleted
    /// when this threshold is exceeded.
    max_mem: usize,
    /// A monotonically increasing counter to track how recently tile sizes were used.
    counter: usize,
}

/// A key with which to store surfaces. It is based on the size of the surface.
#[derive(Eq, Copy, Clone)]
struct SurfaceKey([i32; 2]);

impl Hash for SurfaceKey {
    fn hash<H: Hasher>(&self, state: &mut H) {
        let SurfaceKey(ref bytes) = *self;
        bytes.hash(state);
    }
}

impl PartialEq for SurfaceKey {
    fn eq(&self, other: &SurfaceKey) -> bool {
        let SurfaceKey(s) = *self;
        let SurfaceKey(o) = *other;
        s[0] == o[0] && s[1] == o[1]
    }
}

/// Create a key from a given size
impl SurfaceKey {
    fn get(input: Size2D<i32>) -> SurfaceKey {
        SurfaceKey([input.width, input.height])
    }
}

/// A helper struct to keep track of surfaces in the HashMap
struct SurfaceValue {
    /// An array of surfaces, all the same size
    surfaces: Vec<NativeSurface>,
    /// The counter when this size was last requested
    last_action: usize,
}

impl SurfaceMap {
    // Creates a new SurfaceMap with a given surface limit.
    pub fn new(max_mem: usize) -> SurfaceMap {
        SurfaceMap {
            map: HashMap::new(),
            mem: 0,
            max_mem: max_mem,
            counter: 0,
        }
    }

    pub fn insert_surfaces<I>(&mut self, display: &NativeDisplay, surfaces: I)
        where I: IntoIterator<Item=NativeSurface>
    {
        for surface in surfaces {
            self.insert(display, surface);
        }
    }

    /// Insert a new buffer into the map.
    pub fn insert(&mut self, display: &NativeDisplay, mut new_surface: NativeSurface) {
        let new_key = SurfaceKey::get(new_surface.get_size());

        // If all our surfaces are the same size and we're already at our
        // memory limit, no need to store this new buffer; just let it drop.
        let new_total_memory_usage = self.mem + new_surface.get_memory_usage();
        if new_total_memory_usage > self.max_mem && self.map.len() == 1 &&
            self.map.contains_key(&new_key) {
            new_surface.destroy(display);
            return;
        }

        self.mem = new_total_memory_usage;
        new_surface.mark_wont_leak();

        // use lazy insertion function to prevent unnecessary allocation
        let counter = &self.counter;
        match self.map.entry(new_key) {
            Occupied(entry) => {
                entry.into_mut().surfaces.push(new_surface);
            }
            Vacant(entry) => {
                entry.insert(SurfaceValue {
                    surfaces: vec!(new_surface),
                    last_action: *counter,
                });
            }
        }

        let mut opt_key: Option<SurfaceKey> = None;
        while self.mem > self.max_mem {
            let old_key = match opt_key {
                Some(key) => key,
                None => {
                    match self.map.iter().min_by_key(|&(_, x)| x.last_action) {
                        Some((k, _)) => *k,
                        None => panic!("SurfaceMap: tried to delete with no elements in map"),
                    }
                }
            };
            if {
                let list = &mut self.map.get_mut(&old_key).unwrap().surfaces;
                let mut condemned_surface = list.pop().take().unwrap();
                self.mem -= condemned_surface.get_memory_usage();
                condemned_surface.destroy(display);
                list.is_empty()
            }
            { // then
                self.map.remove(&old_key); // Don't store empty vectors!
                opt_key = None;
            } else {
                opt_key = Some(old_key);
            }
        }
    }

    // Try to find a buffer for the given size.
    pub fn find(&mut self, size: Size2D<i32>) -> Option<NativeSurface> {
        let mut flag = false; // True if key needs to be popped after retrieval.
        let key = SurfaceKey::get(size);
        let ret = match self.map.get_mut(&key) {
            Some(ref mut surface_val) => {
                surface_val.last_action = self.counter;
                self.counter += 1;

                let surface = surface_val.surfaces.pop().take().unwrap();
                self.mem -= surface.get_memory_usage();
                if surface_val.surfaces.is_empty() {
                    flag = true;
                }
                Some(surface)
            }
            None => None,
        };

        if flag {
            self.map.remove(&key); // Don't store empty vectors!
        }

        ret
    }

    pub fn mem(&self) -> usize {
        self.mem
    }
}
<|endoftext|>


<commit_before>use LalafellBot;
use config::Listener;
use listeners::ReceivesEvents;
use discord::model::{Event, Channel, ReactionEmoji};

use xivdb::error::*;

use serde_json;

use std::sync::Arc;

pub struct ReactionAuthorize {
  bot: Arc<LalafellBot>,
  config: ReactionAuthorizeConfig
}

impl ReactionAuthorize {
  pub fn new(bot: Arc<LalafellBot>, listener: &Listener) -> Result<ReactionAuthorize> {
    let config = match listener.config {
      Some(ref c) => serde_json::from_value(c.clone()).chain_err(|| "could not parse reaction_authorize configuration")?,
      None => return Err("missing configuration for reaction_authorize listener".into())
    };
    Ok(ReactionAuthorize {
      bot: bot,
      config: config
    })
  }
}

impl ReceivesEvents for ReactionAuthorize {
  fn receive(&self, event: &Event) {
    let (added, reaction) = match *event {
      Event::ReactionAdd(ref reaction) => (true, reaction),
      Event::ReactionRemove(ref reaction) => (false, reaction),
      _ => return
    };
    let channel = match self.bot.discord.get_channel(reaction.channel_id) {
      Ok(Channel::Public(c)) => c,
      Ok(_) => {
        warn!("invalid channel: {}", reaction.channel_id.0);
        return;
      },
      Err(e) => {
        warn!("couldn't get channel: {}", e);
        return;
      }
    };
    match reaction.emoji {
      ReactionEmoji::Unicode(ref emoji) if *emoji == self.config.emoji => {},
      _ => return
    }
    let roles = match self.bot.discord.get_roles(channel.server_id) {
      Ok(r) => r,
      Err(e) => {
        warn!("couldn't get roles: {}", e);
        return;
      }
    };
    let role = match roles.iter().filter(|r| r.name == self.config.role).next() {
      Some(r) => r,
      None => {
        warn!("couldn't find role for name \"{}\"", self.config.role);
        return;
      }
    };
    let result = if added {
      self.bot.discord.add_user_to_role(channel.server_id, reaction.user_id, role.id)
    } else {
      self.bot.discord.remove_user_from_role(channel.server_id, reaction.user_id, role.id)
    };
    if let Err(e) = result {
      warn!("could not authorize: {}", e);
    }
  }
}

#[derive(Debug, Deserialize)]
pub struct ReactionAuthorizeConfig {
  message: u64,
  emoji: String,
  role: String
}
<commit_msg>fix: change error message<commit_after>use LalafellBot;
use config::Listener;
use listeners::ReceivesEvents;
use discord::model::{Event, Channel, ReactionEmoji};

use xivdb::error::*;

use serde_json;

use std::sync::Arc;

pub struct ReactionAuthorize {
  bot: Arc<LalafellBot>,
  config: ReactionAuthorizeConfig
}

impl ReactionAuthorize {
  pub fn new(bot: Arc<LalafellBot>, listener: &Listener) -> Result<ReactionAuthorize> {
    let config = match listener.config {
      Some(ref c) => serde_json::from_value(c.clone()).chain_err(|| "could not parse reaction_authorize configuration")?,
      None => return Err("missing configuration for reaction_authorize listener".into())
    };
    Ok(ReactionAuthorize {
      bot: bot,
      config: config
    })
  }
}

impl ReceivesEvents for ReactionAuthorize {
  fn receive(&self, event: &Event) {
    let (added, reaction) = match *event {
      Event::ReactionAdd(ref reaction) => (true, reaction),
      Event::ReactionRemove(ref reaction) => (false, reaction),
      _ => return
    };
    let channel = match self.bot.discord.get_channel(reaction.channel_id) {
      Ok(Channel::Public(c)) => c,
      Ok(_) => {
        warn!("invalid channel: {}", reaction.channel_id.0);
        return;
      },
      Err(e) => {
        warn!("couldn't get channel: {}", e);
        return;
      }
    };
    match reaction.emoji {
      ReactionEmoji::Unicode(ref emoji) if *emoji == self.config.emoji => {},
      _ => return
    }
    let roles = match self.bot.discord.get_roles(channel.server_id) {
      Ok(r) => r,
      Err(e) => {
        warn!("couldn't get roles: {}", e);
        return;
      }
    };
    let role = match roles.iter().filter(|r| r.name == self.config.role).next() {
      Some(r) => r,
      None => {
        warn!("couldn't find role for name \"{}\"", self.config.role);
        return;
      }
    };
    let result = if added {
      self.bot.discord.add_user_to_role(channel.server_id, reaction.user_id, role.id)
    } else {
      self.bot.discord.remove_user_from_role(channel.server_id, reaction.user_id, role.id)
    };
    if let Err(e) = result {
      warn!("could not change role: {}", e);
    }
  }
}

#[derive(Debug, Deserialize)]
pub struct ReactionAuthorizeConfig {
  message: u64,
  emoji: String,
  role: String
}
<|endoftext|>


<commit_before>#![feature(cfg_target_feature, stdsimd)]
#![cfg_attr(feature = "strict", deny(warnings))]

extern crate core;
extern crate coresimd;

use core::{mem, slice};
use coresimd::simd::*;

#[test]
fn endian_indexing() {
    let v = i32x4::new(0, 1, 2, 3);
    assert_eq!(v.extract(0), 0);
    assert_eq!(v.extract(1), 1);
    assert_eq!(v.extract(2), 2);
    assert_eq!(v.extract(3), 3);
}

#[test]
fn endian_bitcasts() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let t: i16x8 = unsafe { mem::transmute(x) };
    if cfg!(target_endian = "little") {
        let t_el = i16x8::new(256, 770, 1284, 1798, 2312, 2826, 3340, 3854);
        assert_eq!(t, t_el);
    } else if cfg!(target_endian = "big") {
        let t_be = i16x8::new(1, 515, 1029, 1543, 2057, 2571, 3085, 3599);
        assert_eq!(t, t_be);
    }
}

#[test]
fn endian_casts() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let t: i16x16 = x.into(); // simd_cast
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i16x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    assert_eq!(t, e);
}

#[test]
fn endian_load_and_stores() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let mut y: [i16; 8] = [0; 8];
    x.store_unaligned(unsafe {
        slice::from_raw_parts_mut(&mut y as *mut _ as *mut i8, 16)
    });

    if cfg!(target_endian = "little") {
        let e: [i16; 8] = [256, 770, 1284, 1798, 2312, 2826, 3340, 3854];
        assert_eq!(y, e);
    } else if cfg!(target_endian = "big") {
        let e: [i16; 8] = [1, 515, 1029, 1543, 2057, 2571, 3085, 3599];
        assert_eq!(y, e);
    }

    let z = i8x16::load_unaligned(unsafe {
        slice::from_raw_parts(&y as *const _ as *const i8, 16)
    });
    assert_eq!(z, x);
}

#[test]
fn endian_array_union() {
    union A {
        data: [f32; 4],
        vec: f32x4,
    }
    let x: [f32; 4] = unsafe {
        A {
            vec: f32x4::new(0., 1., 2., 3.),
        }.data
    };
    assert_eq!(x[0], 0_f32);
    assert_eq!(x[1], 1_f32);
    assert_eq!(x[2], 2_f32);
    assert_eq!(x[3], 3_f32);
    let y: f32x4 = unsafe {
        A {
            data: [3., 2., 1., 0.],
        }.vec
    };
    assert_eq!(y, f32x4::new(3., 2., 1., 0.));

    union B {
        data: [i8; 16],
        vec: i8x16,
    }
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: [i8; 16] = unsafe { B { vec: x }.data };

    for i in 0..16 {
        assert_eq!(x[i], i as i8);
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let y = [
        15, 14, 13, 12, 11, 19, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    ];
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i8x16::new(
        15, 14, 13, 12, 11, 19, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    let z = unsafe { B { data: y }.vec };
    assert_eq!(z, e);
}

#[test]
fn endian_tuple_access() {
    type F32x4T = (f32, f32, f32, f32);
    union A {
        data: F32x4T,
        vec: f32x4,
    }
    let x: F32x4T = unsafe {
        A {
            vec: f32x4::new(0., 1., 2., 3.),
        }.data
    };
    assert_eq!(x.0, 0_f32);
    assert_eq!(x.1, 1_f32);
    assert_eq!(x.2, 2_f32);
    assert_eq!(x.3, 3_f32);
    let y: f32x4 = unsafe {
        A {
            data: (3., 2., 1., 0.),
        }.vec
    };
    assert_eq!(y, f32x4::new(3., 2., 1., 0.));

    #[cfg_attr(rustfmt, rustfmt_skip)]
    type I8x16T = (i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8);
    union B {
        data: I8x16T,
        vec: i8x16,
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: I8x16T = unsafe { B { vec: x }.data };

    assert_eq!(x.0, 0);
    assert_eq!(x.1, 1);
    assert_eq!(x.2, 2);
    assert_eq!(x.3, 3);
    assert_eq!(x.4, 4);
    assert_eq!(x.5, 5);
    assert_eq!(x.6, 6);
    assert_eq!(x.7, 7);
    assert_eq!(x.8, 8);
    assert_eq!(x.9, 9);
    assert_eq!(x.10, 10);
    assert_eq!(x.11, 11);
    assert_eq!(x.12, 12);
    assert_eq!(x.13, 13);
    assert_eq!(x.14, 14);
    assert_eq!(x.15, 15);

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let y = (
        15, 14, 13, 12, 11, 10, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    let z: i8x16 = unsafe { B { data: y }.vec };
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i8x16::new(
        15, 14, 13, 12, 11, 10, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    assert_eq!(e, z);
}
<commit_msg>endian-dependent conversions to/from tuples tests (#400)<commit_after>#![feature(cfg_target_feature, stdsimd)]
#![cfg_attr(feature = "strict", deny(warnings))]

extern crate core;
extern crate coresimd;

use core::{mem, slice};
use coresimd::simd::*;

#[test]
fn endian_indexing() {
    let v = i32x4::new(0, 1, 2, 3);
    assert_eq!(v.extract(0), 0);
    assert_eq!(v.extract(1), 1);
    assert_eq!(v.extract(2), 2);
    assert_eq!(v.extract(3), 3);
}

#[test]
fn endian_bitcasts() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let t: i16x8 = unsafe { mem::transmute(x) };
    let e: i16x8 = if cfg!(target_endian = "little") {
        i16x8::new(256, 770, 1284, 1798, 2312, 2826, 3340, 3854)
    } else {
        i16x8::new(1, 515, 1029, 1543, 2057, 2571, 3085, 3599)
    };
    assert_eq!(t, e);
}

#[test]
fn endian_casts() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let t: i16x16 = x.into(); // simd_cast
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i16x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    assert_eq!(t, e);
}

#[test]
fn endian_load_and_stores() {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let mut y: [i16; 8] = [0; 8];
    x.store_unaligned(unsafe {
        slice::from_raw_parts_mut(&mut y as *mut _ as *mut i8, 16)
    });

    let e: [i16; 8] = if cfg!(target_endian = "little") {
        [256, 770, 1284, 1798, 2312, 2826, 3340, 3854]
    } else {
        [1, 515, 1029, 1543, 2057, 2571, 3085, 3599]
    };
    assert_eq!(y, e);

    let z = i8x16::load_unaligned(unsafe {
        slice::from_raw_parts(&y as *const _ as *const i8, 16)
    });
    assert_eq!(z, x);
}

#[test]
fn endian_array_union() {
    union A {
        data: [f32; 4],
        vec: f32x4,
    }
    let x: [f32; 4] = unsafe {
        A {
            vec: f32x4::new(0., 1., 2., 3.),
        }.data
    };
    assert_eq!(x[0], 0_f32);
    assert_eq!(x[1], 1_f32);
    assert_eq!(x[2], 2_f32);
    assert_eq!(x[3], 3_f32);
    let y: f32x4 = unsafe {
        A {
            data: [3., 2., 1., 0.],
        }.vec
    };
    assert_eq!(y, f32x4::new(3., 2., 1., 0.));

    union B {
        data: [i8; 16],
        vec: i8x16,
    }
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: [i8; 16] = unsafe { B { vec: x }.data };

    for i in 0..16 {
        assert_eq!(x[i], i as i8);
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let y = [
        15, 14, 13, 12, 11, 19, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    ];
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i8x16::new(
        15, 14, 13, 12, 11, 19, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    let z = unsafe { B { data: y }.vec };
    assert_eq!(z, e);

    union C {
        data: [i16; 8],
        vec: i8x16,
    }
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: [i16; 8] = unsafe { C { vec: x }.data };

    let e: [i16; 8] = if cfg!(target_endian = "little") {
        [256, 770, 1284, 1798, 2312, 2826, 3340, 3854]
    } else {
        [1, 515, 1029, 1543, 2057, 2571, 3085, 3599]
    };
    assert_eq!(x, e);
}

#[test]
fn endian_tuple_access() {
    type F32x4T = (f32, f32, f32, f32);
    union A {
        data: F32x4T,
        vec: f32x4,
    }
    let x: F32x4T = unsafe {
        A {
            vec: f32x4::new(0., 1., 2., 3.),
        }.data
    };
    assert_eq!(x.0, 0_f32);
    assert_eq!(x.1, 1_f32);
    assert_eq!(x.2, 2_f32);
    assert_eq!(x.3, 3_f32);
    let y: f32x4 = unsafe {
        A {
            data: (3., 2., 1., 0.),
        }.vec
    };
    assert_eq!(y, f32x4::new(3., 2., 1., 0.));

    #[cfg_attr(rustfmt, rustfmt_skip)]
    type I8x16T = (i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8, i8);
    union B {
        data: I8x16T,
        vec: i8x16,
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: I8x16T = unsafe { B { vec: x }.data };

    assert_eq!(x.0, 0);
    assert_eq!(x.1, 1);
    assert_eq!(x.2, 2);
    assert_eq!(x.3, 3);
    assert_eq!(x.4, 4);
    assert_eq!(x.5, 5);
    assert_eq!(x.6, 6);
    assert_eq!(x.7, 7);
    assert_eq!(x.8, 8);
    assert_eq!(x.9, 9);
    assert_eq!(x.10, 10);
    assert_eq!(x.11, 11);
    assert_eq!(x.12, 12);
    assert_eq!(x.13, 13);
    assert_eq!(x.14, 14);
    assert_eq!(x.15, 15);

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let y = (
        15, 14, 13, 12, 11, 10, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    let z: i8x16 = unsafe { B { data: y }.vec };
    #[cfg_attr(rustfmt, rustfmt_skip)]
    let e = i8x16::new(
        15, 14, 13, 12, 11, 10, 9, 8,
        7, 6, 5, 4, 3, 2, 1, 0
    );
    assert_eq!(e, z);

    #[cfg_attr(rustfmt, rustfmt_skip)]
    type I16x8T = (i16, i16, i16, i16, i16, i16, i16, i16);
    union C {
        data: I16x8T,
        vec: i8x16,
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: I16x8T = unsafe { C { vec: x }.data };

    let e: [i16; 8] = if cfg!(target_endian = "little") {
        [256, 770, 1284, 1798, 2312, 2826, 3340, 3854]
    } else {
        [1, 515, 1029, 1543, 2057, 2571, 3085, 3599]
    };
    assert_eq!(x.0, e[0]);
    assert_eq!(x.1, e[1]);
    assert_eq!(x.2, e[2]);
    assert_eq!(x.3, e[3]);
    assert_eq!(x.4, e[4]);
    assert_eq!(x.5, e[5]);
    assert_eq!(x.6, e[6]);
    assert_eq!(x.7, e[7]);

    // Without repr(C) this produces total garbage.
    // FIXME: investigate more, this is maybe due to
    // to tuple field reordering to minimize padding.
    #[cfg_attr(rustfmt, rustfmt_skip)]
    #[repr(C)]
    #[derive(Copy ,Clone)]
    pub struct Tup(pub i8, pub i8, pub i16, pub i8, pub i8, pub i16,
                   pub i8, pub i8, pub i16, pub i8, pub i8, pub i16);

    union D {
        data: Tup,
        vec: i8x16,
    }

    #[cfg_attr(rustfmt, rustfmt_skip)]
    let x = i8x16::new(
        0, 1, 2, 3, 4, 5, 6, 7,
        8, 9, 10, 11, 12, 13, 14, 15,
    );
    let x: Tup = unsafe { D { vec: x }.data };

    let e: [i16; 12] = if cfg!(target_endian = "little") {
        [0, 1, 770, 4, 5, 1798, 8, 9, 2826, 12, 13, 3854]
    } else {
        [0, 1, 515, 4, 5, 1543, 8, 9, 2571, 12, 13, 3599]
    };
    assert_eq!(x.0 as i16, e[0]);
    assert_eq!(x.1 as i16, e[1]);
    assert_eq!(x.2 as i16, e[2]);
    assert_eq!(x.3 as i16, e[3]);
    assert_eq!(x.4 as i16, e[4]);
    assert_eq!(x.5 as i16, e[5]);
    assert_eq!(x.6 as i16, e[6]);
    assert_eq!(x.7 as i16, e[7]);
    assert_eq!(x.8 as i16, e[8]);
    assert_eq!(x.9 as i16, e[9]);
    assert_eq!(x.10 as i16, e[10]);
    assert_eq!(x.11 as i16, e[11]);
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use stdx::format_to;
use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};

pub(crate) trait ShortLabel {
    fn short_label(&self) -> Option<String>;
}

impl ShortLabel for ast::Fn {
    fn short_label(&self) -> Option<String> {
        Some(crate::display::function_declaration(self))
    }
}

impl ShortLabel for ast::Struct {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "struct ")
    }
}

impl ShortLabel for ast::Union {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "union ")
    }
}

impl ShortLabel for ast::Enum {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "enum ")
    }
}

impl ShortLabel for ast::Trait {
    fn short_label(&self) -> Option<String> {
        if self.unsafe_token().is_some() {
            short_label_from_node(self, "unsafe trait ")
        } else {
            short_label_from_node(self, "trait ")
        }
    }
}

impl ShortLabel for ast::Module {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "mod ")
    }
}

impl ShortLabel for ast::SourceFile {
    fn short_label(&self) -> Option<String> {
        None
    }
}

impl ShortLabel for ast::BlockExpr {
    fn short_label(&self) -> Option<String> {
        None
    }
}

impl ShortLabel for ast::TypeAlias {
    fn short_label(&self) -> Option<String> {
        let mut buf = short_label_from_node(self, "type ")?;
        if let Some(type_ref) = self.ty() {
            format_to!(buf, " = {}", type_ref.syntax());
        }
        Some(buf)
    }
}

impl ShortLabel for ast::Const {
    fn short_label(&self) -> Option<String> {
        let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?;
        if let Some(expr) = self.body() {
            format_to!(new_buf, " = {}", expr.syntax());
        }
        Some(new_buf)
    }
}

impl ShortLabel for ast::Static {
    fn short_label(&self) -> Option<String> {
        short_label_from_ty(self, self.ty(), "static ")
    }
}

impl ShortLabel for ast::RecordField {
    fn short_label(&self) -> Option<String> {
        short_label_from_ty(self, self.ty(), "")
    }
}

impl ShortLabel for ast::Variant {
    fn short_label(&self) -> Option<String> {
        Some(self.name()?.text().to_string())
    }
}

impl ShortLabel for ast::ConstParam {
    fn short_label(&self) -> Option<String> {
        let mut buf = "const ".to_owned();
        buf.push_str(self.name()?.text());
        if let Some(type_ref) = self.ty() {
            format_to!(buf, ": {}", type_ref.syntax());
        }
        Some(buf)
    }
}

fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String>
where
    T: NameOwner + VisibilityOwner,
{
    let mut buf = short_label_from_node(node, prefix)?;

    if let Some(type_ref) = ty {
        format_to!(buf, ": {}", type_ref.syntax());
    }

    Some(buf)
}

fn short_label_from_node<T>(node: &T, label: &str) -> Option<String>
where
    T: NameOwner + VisibilityOwner,
{
    let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
    buf.push_str(label);
    buf.push_str(node.name()?.text());
    Some(buf)
}
<commit_msg>Don't show const items initializer expressions on hover<commit_after>//! FIXME: write short doc here

use stdx::format_to;
use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};

pub(crate) trait ShortLabel {
    fn short_label(&self) -> Option<String>;
}

impl ShortLabel for ast::Fn {
    fn short_label(&self) -> Option<String> {
        Some(crate::display::function_declaration(self))
    }
}

impl ShortLabel for ast::Struct {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "struct ")
    }
}

impl ShortLabel for ast::Union {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "union ")
    }
}

impl ShortLabel for ast::Enum {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "enum ")
    }
}

impl ShortLabel for ast::Trait {
    fn short_label(&self) -> Option<String> {
        if self.unsafe_token().is_some() {
            short_label_from_node(self, "unsafe trait ")
        } else {
            short_label_from_node(self, "trait ")
        }
    }
}

impl ShortLabel for ast::Module {
    fn short_label(&self) -> Option<String> {
        short_label_from_node(self, "mod ")
    }
}

impl ShortLabel for ast::SourceFile {
    fn short_label(&self) -> Option<String> {
        None
    }
}

impl ShortLabel for ast::BlockExpr {
    fn short_label(&self) -> Option<String> {
        None
    }
}

impl ShortLabel for ast::TypeAlias {
    fn short_label(&self) -> Option<String> {
        let mut buf = short_label_from_node(self, "type ")?;
        if let Some(type_ref) = self.ty() {
            format_to!(buf, " = {}", type_ref.syntax());
        }
        Some(buf)
    }
}

impl ShortLabel for ast::Const {
    fn short_label(&self) -> Option<String> {
        short_label_from_ty(self, self.ty(), "const ")
    }
}

impl ShortLabel for ast::Static {
    fn short_label(&self) -> Option<String> {
        short_label_from_ty(self, self.ty(), "static ")
    }
}

impl ShortLabel for ast::RecordField {
    fn short_label(&self) -> Option<String> {
        short_label_from_ty(self, self.ty(), "")
    }
}

impl ShortLabel for ast::Variant {
    fn short_label(&self) -> Option<String> {
        Some(self.name()?.text().to_string())
    }
}

impl ShortLabel for ast::ConstParam {
    fn short_label(&self) -> Option<String> {
        let mut buf = "const ".to_owned();
        buf.push_str(self.name()?.text());
        if let Some(type_ref) = self.ty() {
            format_to!(buf, ": {}", type_ref.syntax());
        }
        Some(buf)
    }
}

fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String>
where
    T: NameOwner + VisibilityOwner,
{
    let mut buf = short_label_from_node(node, prefix)?;

    if let Some(type_ref) = ty {
        format_to!(buf, ": {}", type_ref.syntax());
    }

    Some(buf)
}

fn short_label_from_node<T>(node: &T, label: &str) -> Option<String>
where
    T: NameOwner + VisibilityOwner,
{
    let mut buf = node.visibility().map(|v| format!("{} ", v.syntax())).unwrap_or_default();
    buf.push_str(label);
    buf.push_str(node.name()?.text());
    Some(buf)
}
<|endoftext|>


<commit_before>use hir::{AsAssocItem, Impl, Semantics};
use ide_db::{
    defs::{Definition, NameClass, NameRefClass},
    RootDatabase,
};
use syntax::{ast, AstNode};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Implementation
//
// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+F12]
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
pub(crate) fn goto_implementation(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(position.file_id);
    let syntax = source_file.syntax().clone();

    let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
    let def = match &node {
        ast::NameLike::Name(name) => {
            NameClass::classify(&sema, name).map(|class| class.referenced_or_defined(sema.db))
        }
        ast::NameLike::NameRef(name_ref) => {
            NameRefClass::classify(&sema, name_ref).map(|class| class.referenced(sema.db))
        }
        ast::NameLike::Lifetime(_) => None,
    }?;

    let def = match def {
        Definition::ModuleDef(def) => def,
        _ => return None,
    };
    let navs = match def {
        hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_),
        hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
        hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
        hir::ModuleDef::BuiltinType(builtin) => {
            let module = sema.to_module_def(position.file_id)?;
            impls_for_ty(&sema, builtin.ty(sema.db, module))
        }
        hir::ModuleDef::Function(f) => {
            let assoc = f.as_assoc_item(sema.db)?;
            let name = assoc.name(sema.db)?;
            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
            impls_for_trait_item(&sema, trait_, name)
        }
        hir::ModuleDef::Const(c) => {
            let assoc = c.as_assoc_item(sema.db)?;
            let name = assoc.name(sema.db)?;
            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
            impls_for_trait_item(&sema, trait_, name)
        }
        _ => return None,
    };
    Some(RangeInfo { range: node.syntax().text_range(), info: navs })
}

fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
    Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
}

fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
    Impl::all_for_trait(sema.db, trait_)
        .into_iter()
        .filter_map(|imp| imp.try_to_nav(sema.db))
        .collect()
}

fn impls_for_trait_item(
    sema: &Semantics<RootDatabase>,
    trait_: hir::Trait,
    fun_name: hir::Name,
) -> Vec<NavigationTarget> {
    Impl::all_for_trait(sema.db, trait_)
        .into_iter()
        .filter_map(|imp| {
            let item = imp.items(sema.db).iter().find_map(|itm| {
                let itm_name = itm.name(sema.db)?;
                (itm_name == fun_name).then(|| *itm)
            })?;
            item.try_to_nav(sema.db)
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, annotations) = fixture::annotations(ra_fixture);

        let navs = analysis.goto_implementation(position).unwrap().unwrap().info;

        let key = |frange: &FileRange| (frange.file_id, frange.range.start());

        let mut expected = annotations
            .into_iter()
            .map(|(range, data)| {
                assert!(data.is_empty());
                range
            })
            .collect::<Vec<_>>();
        expected.sort_by_key(key);

        let mut actual = navs
            .into_iter()
            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
            .collect::<Vec<_>>();
        actual.sort_by_key(key);

        assert_eq!(expected, actual);
    }

    #[test]
    fn goto_implementation_works() {
        check(
            r#"
struct Foo$0;
impl Foo {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_blocks() {
        check(
            r#"
struct Foo$0;
impl Foo {}
   //^^^
impl Foo {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_mods() {
        check(
            r#"
struct Foo$0;
mod a {
    impl super::Foo {}
       //^^^^^^^^^^
}
mod b {
    impl super::Foo {}
       //^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_files() {
        check(
            r#"
//- /lib.rs
struct Foo$0;
mod a;
mod b;
//- /a.rs
impl crate::Foo {}
   //^^^^^^^^^^
//- /b.rs
impl crate::Foo {}
   //^^^^^^^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_for_trait() {
        check(
            r#"
trait T$0 {}
struct Foo;
impl T for Foo {}
         //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_for_trait_multiple_files() {
        check(
            r#"
//- /lib.rs
trait T$0 {};
struct Foo;
mod a;
mod b;
//- /a.rs
impl crate::T for crate::Foo {}
                //^^^^^^^^^^
//- /b.rs
impl crate::T for crate::Foo {}
                //^^^^^^^^^^
            "#,
        );
    }

    #[test]
    fn goto_implementation_all_impls() {
        check(
            r#"
//- /lib.rs
trait T {}
struct Foo$0;
impl Foo {}
   //^^^
impl T for Foo {}
         //^^^
impl T for &Foo {}
         //^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_to_builtin_derive() {
        check(
            r#"
  #[derive(Copy)]
//^^^^^^^^^^^^^^^
struct Foo$0;

mod marker {
    trait Copy {}
}
#[rustc_builtin_macro]
macro Copy {}
"#,
        );
    }

    #[test]
    fn goto_implementation_type_alias() {
        check(
            r#"
struct Foo;

type Bar$0 = Foo;

impl Foo {}
   //^^^
impl Bar {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_adt_generic() {
        check(
            r#"
struct Foo$0<T>;

impl<T> Foo<T> {}
      //^^^^^^
impl Foo<str> {}
   //^^^^^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_builtin() {
        check(
            r#"
//- /lib.rs crate:main deps:core
fn foo(_: bool$0) {{}}
//- /libcore.rs crate:core
#[lang = "bool"]
impl bool {}
   //^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_trait_functions() {
        check(
            r#"
trait Tr {
    fn f$0();
}

struct S;

impl Tr for S {
    fn f() {
     //^
        println!("Hello, world!");
    }
}
"#,
        );
    }

    #[test]
    fn goto_implementation_trait_assoc_const() {
        check(
            r#"
trait Tr {
    const C$0: usize;
}

struct S;

impl Tr for S {
    const C: usize = 4;
        //^
}
"#,
        );
    }
}
<commit_msg>minor: use minicore<commit_after>use hir::{AsAssocItem, Impl, Semantics};
use ide_db::{
    defs::{Definition, NameClass, NameRefClass},
    RootDatabase,
};
use syntax::{ast, AstNode};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Implementation
//
// Navigates to the impl block of structs, enums or traits. Also implemented as a code lens.
//
// |===
// | Editor  | Shortcut
//
// | VS Code | kbd:[Ctrl+F12]
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113065566-02f85480-91b1-11eb-9288-aaad8abd8841.gif[]
pub(crate) fn goto_implementation(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = Semantics::new(db);
    let source_file = sema.parse(position.file_id);
    let syntax = source_file.syntax().clone();

    let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?;
    let def = match &node {
        ast::NameLike::Name(name) => {
            NameClass::classify(&sema, name).map(|class| class.referenced_or_defined(sema.db))
        }
        ast::NameLike::NameRef(name_ref) => {
            NameRefClass::classify(&sema, name_ref).map(|class| class.referenced(sema.db))
        }
        ast::NameLike::Lifetime(_) => None,
    }?;

    let def = match def {
        Definition::ModuleDef(def) => def,
        _ => return None,
    };
    let navs = match def {
        hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_),
        hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)),
        hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)),
        hir::ModuleDef::BuiltinType(builtin) => {
            let module = sema.to_module_def(position.file_id)?;
            impls_for_ty(&sema, builtin.ty(sema.db, module))
        }
        hir::ModuleDef::Function(f) => {
            let assoc = f.as_assoc_item(sema.db)?;
            let name = assoc.name(sema.db)?;
            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
            impls_for_trait_item(&sema, trait_, name)
        }
        hir::ModuleDef::Const(c) => {
            let assoc = c.as_assoc_item(sema.db)?;
            let name = assoc.name(sema.db)?;
            let trait_ = assoc.containing_trait_or_trait_impl(sema.db)?;
            impls_for_trait_item(&sema, trait_, name)
        }
        _ => return None,
    };
    Some(RangeInfo { range: node.syntax().text_range(), info: navs })
}

fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> {
    Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()
}

fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> {
    Impl::all_for_trait(sema.db, trait_)
        .into_iter()
        .filter_map(|imp| imp.try_to_nav(sema.db))
        .collect()
}

fn impls_for_trait_item(
    sema: &Semantics<RootDatabase>,
    trait_: hir::Trait,
    fun_name: hir::Name,
) -> Vec<NavigationTarget> {
    Impl::all_for_trait(sema.db, trait_)
        .into_iter()
        .filter_map(|imp| {
            let item = imp.items(sema.db).iter().find_map(|itm| {
                let itm_name = itm.name(sema.db)?;
                (itm_name == fun_name).then(|| *itm)
            })?;
            item.try_to_nav(sema.db)
        })
        .collect()
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, annotations) = fixture::annotations(ra_fixture);

        let navs = analysis.goto_implementation(position).unwrap().unwrap().info;

        let key = |frange: &FileRange| (frange.file_id, frange.range.start());

        let mut expected = annotations
            .into_iter()
            .map(|(range, data)| {
                assert!(data.is_empty());
                range
            })
            .collect::<Vec<_>>();
        expected.sort_by_key(key);

        let mut actual = navs
            .into_iter()
            .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
            .collect::<Vec<_>>();
        actual.sort_by_key(key);

        assert_eq!(expected, actual);
    }

    #[test]
    fn goto_implementation_works() {
        check(
            r#"
struct Foo$0;
impl Foo {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_blocks() {
        check(
            r#"
struct Foo$0;
impl Foo {}
   //^^^
impl Foo {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_mods() {
        check(
            r#"
struct Foo$0;
mod a {
    impl super::Foo {}
       //^^^^^^^^^^
}
mod b {
    impl super::Foo {}
       //^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn goto_implementation_works_multiple_files() {
        check(
            r#"
//- /lib.rs
struct Foo$0;
mod a;
mod b;
//- /a.rs
impl crate::Foo {}
   //^^^^^^^^^^
//- /b.rs
impl crate::Foo {}
   //^^^^^^^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_for_trait() {
        check(
            r#"
trait T$0 {}
struct Foo;
impl T for Foo {}
         //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_for_trait_multiple_files() {
        check(
            r#"
//- /lib.rs
trait T$0 {};
struct Foo;
mod a;
mod b;
//- /a.rs
impl crate::T for crate::Foo {}
                //^^^^^^^^^^
//- /b.rs
impl crate::T for crate::Foo {}
                //^^^^^^^^^^
            "#,
        );
    }

    #[test]
    fn goto_implementation_all_impls() {
        check(
            r#"
//- /lib.rs
trait T {}
struct Foo$0;
impl Foo {}
   //^^^
impl T for Foo {}
         //^^^
impl T for &Foo {}
         //^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_to_builtin_derive() {
        check(
            r#"
//- minicore: copy, derive
  #[derive(Copy)]
//^^^^^^^^^^^^^^^
struct Foo$0;
"#,
        );
    }

    #[test]
    fn goto_implementation_type_alias() {
        check(
            r#"
struct Foo;

type Bar$0 = Foo;

impl Foo {}
   //^^^
impl Bar {}
   //^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_adt_generic() {
        check(
            r#"
struct Foo$0<T>;

impl<T> Foo<T> {}
      //^^^^^^
impl Foo<str> {}
   //^^^^^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_builtin() {
        check(
            r#"
//- /lib.rs crate:main deps:core
fn foo(_: bool$0) {{}}
//- /libcore.rs crate:core
#[lang = "bool"]
impl bool {}
   //^^^^
"#,
        );
    }

    #[test]
    fn goto_implementation_trait_functions() {
        check(
            r#"
trait Tr {
    fn f$0();
}

struct S;

impl Tr for S {
    fn f() {
     //^
        println!("Hello, world!");
    }
}
"#,
        );
    }

    #[test]
    fn goto_implementation_trait_assoc_const() {
        check(
            r#"
trait Tr {
    const C$0: usize;
}

struct S;

impl Tr for S {
    const C: usize = 4;
        //^
}
"#,
        );
    }
}
<|endoftext|>