<commit_before>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

mod command;
mod cursor;
mod insert;

use rustty::{self, Event, Color};
use editor;

/// `Term` represents our client application. This allows
/// us to work with the filesystem and the UI.
pub struct Term<'a> {
    /// Represents the location of the cursor in our UI
    cursor: cursor::Cursor,
    /// Contains file information; allows us to work with
    /// the filesystem
    editor: &'a mut editor::Editor<'a>,
    /// This is the UI itself
    term: rustty::Terminal,
    /// Represents the running status
    quit: bool,
    /// Represents the topmost line in our UI
    line: usize,
    /// Represents the total number of lines in the currently
    /// open file
    total_lines: usize,
}

impl<'a> Term<'a> {
    pub fn new(editor: &'a mut editor::Editor<'a>) -> Term<'a> {
        Term {
            cursor: cursor::Cursor::new(Color::Red),
            editor: editor,
            term: rustty::Terminal::new().unwrap(),
            quit: false,
            line: 0,
            total_lines: 0,
        }
    }

    /// Launches the terminal.
    pub fn run(&mut self) {
        self.editor.read_file(self.term.cols());
        let lines = self.editor.contents.len() as isize;
        self.set_total_lines(lines);

        self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
        let current_line = self.current_line();
        self.print_file(current_line);
        self.prompt();
        self.term.swap_buffers().unwrap();

        while !self.quit {
            let evt = self.term.get_event(100).unwrap();
            if let Some(Event::Key(ch)) = evt {
                if self.editor.command_mode() {
                    command::handle(self, ch);
                } else {
                    insert::handle(self, ch);
                }

                if self.cursor.pos.x > self.term.cols() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y += 1;
                }
                if self.cursor.pos.y >= self.term.rows() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y = 0;
                }

                self.term[self.cursor.last_pos()].set_bg(Color::Default);
                self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
                self.term.swap_buffers().unwrap();
            }
        }
    }

    /// Prints our prompt to the UI.
    pub fn prompt(&mut self) {
        let w = self.term.cols();
        let h = self.term.rows();

        for i in 0..w {
            self.term[(i, h - 2)].set_bg(Color::Red);
        }

        for (i, c) in self.editor.filename().chars().enumerate() {
            self.term[(i, h - 2)].set_ch(c);
        }
    }

    /// Prints our editor's contents to the UI.
    fn print_file(&mut self, start: usize) {
        let w = self.term.cols();
        let mut i = 0;
        let mut j = 0;
        for line in &self.editor.contents {
            if i < start {
                i += 1;
                continue;
            }

            for &b in line {
                if b == b'\n' {
                    continue;
                }
                self.term[(j, i - start)].set_ch(b as char);
                j += 1;
            }
            while j < w {
                self.term[(j, i - start)].set_ch(' ');
                j += 1;
            }

            i += 1;
            j = 0;
            if i - start == self.term.rows() - 2 {
                break;
            }
        }
    }

    pub fn move_cursor(&mut self, direction: cursor::Direction) {
        match direction {
            cursor::Direction::Up => {
                if self.cursor.pos.y != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.y -= 1;
                } else if self.current_line() != 0 {
                    let current_line = self.current_line();
                    self.set_current_line(current_line - 1);
                    self.redraw_file();
                }
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.cursor.pos.x > len {
                    if self.editor.contents[curr][len] == b'\n' && len > 0{
                        self.cursor.pos.x = len - 1;
                    } else {
                        self.cursor.pos.x = len;
                    }
                }
            }
            cursor::Direction::Down => {
                if self.total_lines() <= self.term.rows() - 3 {
                    if self.cursor.pos.y != self.term.rows() - 3 &&
                       self.cursor.pos.y < self.total_lines() - 1
                    {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    }
                } else {
                    if self.cursor.pos.y != self.term.rows() - 3 {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    } else if self.cursor.pos.y + self.current_line() <
                              self.total_lines() - 1
                    {
                        let current_line = self.current_line();
                        self.set_current_line(current_line + 1);
                        self.redraw_file();
                    }
                }
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.cursor.pos.x > len {
                    if self.editor.contents[curr][len] == b'\n' && len > 0{
                        self.cursor.pos.x = len - 1;
                    } else {
                        self.cursor.pos.x = len;
                    }
                }
            }
            cursor::Direction::Left => {
                if self.cursor.pos.x != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.x -= 1;
                }
            }
            cursor::Direction::Right => {
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.editor.contents[curr][len] == b'\n' {
                    if self.cursor.pos.x != len - 1 {
                        self.cursor.save_pos();
                        self.cursor.pos.x += 1;
                    }
                } else if self.cursor.pos.x != len {
                    self.cursor.save_pos();
                    self.cursor.pos.x += 1;
                }
            }
        }
    }

    /// Changes the value of the `quit` attribute to `true`.
    pub fn quit(&mut self) {
        self.quit = true;
    }

    /// Returns the total lines in our file
    pub fn total_lines(&self) -> usize {
        self.total_lines
    }

    /// Allows us to modify the total number of lines
    /// in our file, e.g. if a new line is added.
    pub fn set_total_lines(&mut self, change: isize) {
        let new = self.total_lines as isize + change;
        self.total_lines = new as usize;
    }

    /// Gives the current line at the top of our UI
    pub fn current_line(&self) -> usize {
        self.line
    }

    /// Allows us to change what the topmost line in the UI is
    pub fn set_current_line(&mut self, line: usize) {
        self.line = line;
    }

    /// This function redraws the portion of our UI displaying
    /// file contents. E.g. this is called when the current line
    /// changes.
    pub fn redraw_file(&mut self) {
        let current_line = self.current_line();
        self.print_file(current_line);
    }

    /// This function redraws the given line of the UI on the screen.
    pub fn redraw_line(&mut self, ui_line: usize) {
        let file_line = ui_line + self.current_line();
        self.print_line(ui_line, file_line);
    }

    pub fn print_line(&mut self, ui_line: usize, file_line: usize) {
        let contents = &self.editor.contents[file_line];
        let mut i = 0;
        for &b in contents {
            if b == b'\n' {
                continue;
            }
            self.term[(i, ui_line)].set_ch(b as char);
            i += 1;
        }
    }
}
<commit_msg>Fix crash when moving right on empty line<commit_after>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

mod command;
mod cursor;
mod insert;

use rustty::{self, Event, Color};
use editor;

/// `Term` represents our client application. This allows
/// us to work with the filesystem and the UI.
pub struct Term<'a> {
    /// Represents the location of the cursor in our UI
    cursor: cursor::Cursor,
    /// Contains file information; allows us to work with
    /// the filesystem
    editor: &'a mut editor::Editor<'a>,
    /// This is the UI itself
    term: rustty::Terminal,
    /// Represents the running status
    quit: bool,
    /// Represents the topmost line in our UI
    line: usize,
    /// Represents the total number of lines in the currently
    /// open file
    total_lines: usize,
}

impl<'a> Term<'a> {
    pub fn new(editor: &'a mut editor::Editor<'a>) -> Term<'a> {
        Term {
            cursor: cursor::Cursor::new(Color::Red),
            editor: editor,
            term: rustty::Terminal::new().unwrap(),
            quit: false,
            line: 0,
            total_lines: 0,
        }
    }

    /// Launches the terminal.
    pub fn run(&mut self) {
        self.editor.read_file(self.term.cols());
        let lines = self.editor.contents.len() as isize;
        self.set_total_lines(lines);

        self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
        let current_line = self.current_line();
        self.print_file(current_line);
        self.prompt();
        self.term.swap_buffers().unwrap();

        while !self.quit {
            let evt = self.term.get_event(100).unwrap();
            if let Some(Event::Key(ch)) = evt {
                if self.editor.command_mode() {
                    command::handle(self, ch);
                } else {
                    insert::handle(self, ch);
                }

                if self.cursor.pos.x > self.term.cols() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y += 1;
                }
                if self.cursor.pos.y >= self.term.rows() - 1 {
                    self.term[self.cursor.last_pos()].set_bg(Color::Default);
                    self.cursor.save_pos();
                    self.cursor.pos.x = 0;
                    self.cursor.pos.y = 0;
                }

                self.term[self.cursor.last_pos()].set_bg(Color::Default);
                self.term[self.cursor.current_pos()].set_bg(self.cursor.color);
                self.term.swap_buffers().unwrap();
            }
        }
    }

    /// Prints our prompt to the UI.
    pub fn prompt(&mut self) {
        let w = self.term.cols();
        let h = self.term.rows();

        for i in 0..w {
            self.term[(i, h - 2)].set_bg(Color::Red);
        }

        for (i, c) in self.editor.filename().chars().enumerate() {
            self.term[(i, h - 2)].set_ch(c);
        }
    }

    /// Prints our editor's contents to the UI.
    fn print_file(&mut self, start: usize) {
        let w = self.term.cols();
        let mut i = 0;
        let mut j = 0;
        for line in &self.editor.contents {
            if i < start {
                i += 1;
                continue;
            }

            for &b in line {
                if b == b'\n' {
                    continue;
                }
                self.term[(j, i - start)].set_ch(b as char);
                j += 1;
            }
            while j < w {
                self.term[(j, i - start)].set_ch(' ');
                j += 1;
            }

            i += 1;
            j = 0;
            if i - start == self.term.rows() - 2 {
                break;
            }
        }
    }

    pub fn move_cursor(&mut self, direction: cursor::Direction) {
        match direction {
            cursor::Direction::Up => {
                if self.cursor.pos.y != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.y -= 1;
                } else if self.current_line() != 0 {
                    let current_line = self.current_line();
                    self.set_current_line(current_line - 1);
                    self.redraw_file();
                }
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.cursor.pos.x > len {
                    if self.editor.contents[curr][len] == b'\n' && len > 0{
                        self.cursor.pos.x = len - 1;
                    } else {
                        self.cursor.pos.x = len;
                    }
                }
            }
            cursor::Direction::Down => {
                if self.total_lines() <= self.term.rows() - 3 {
                    if self.cursor.pos.y != self.term.rows() - 3 &&
                       self.cursor.pos.y < self.total_lines() - 1
                    {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    }
                } else {
                    if self.cursor.pos.y != self.term.rows() - 3 {
                        self.cursor.save_pos();
                        self.cursor.pos.y += 1;
                    } else if self.cursor.pos.y + self.current_line() <
                              self.total_lines() - 1
                    {
                        let current_line = self.current_line();
                        self.set_current_line(current_line + 1);
                        self.redraw_file();
                    }
                }
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.cursor.pos.x > len {
                    if self.editor.contents[curr][len] == b'\n' && len > 0{
                        self.cursor.pos.x = len - 1;
                    } else {
                        self.cursor.pos.x = len;
                    }
                }
            }
            cursor::Direction::Left => {
                if self.cursor.pos.x != 0 {
                    self.cursor.save_pos();
                    self.cursor.pos.x -= 1;
                }
            }
            cursor::Direction::Right => {
                let curr = self.cursor.pos.y + self.current_line();
                let len = self.editor.contents[curr].len() - 1;
                if self.editor.contents[curr][len] == b'\n' {
                    if len > 0 && self.cursor.pos.x != len - 1 {
                        self.cursor.save_pos();
                        self.cursor.pos.x += 1;
                    }
                } else if self.cursor.pos.x != len {
                    self.cursor.save_pos();
                    self.cursor.pos.x += 1;
                }
            }
        }
    }

    /// Changes the value of the `quit` attribute to `true`.
    pub fn quit(&mut self) {
        self.quit = true;
    }

    /// Returns the total lines in our file
    pub fn total_lines(&self) -> usize {
        self.total_lines
    }

    /// Allows us to modify the total number of lines
    /// in our file, e.g. if a new line is added.
    pub fn set_total_lines(&mut self, change: isize) {
        let new = self.total_lines as isize + change;
        self.total_lines = new as usize;
    }

    /// Gives the current line at the top of our UI
    pub fn current_line(&self) -> usize {
        self.line
    }

    /// Allows us to change what the topmost line in the UI is
    pub fn set_current_line(&mut self, line: usize) {
        self.line = line;
    }

    /// This function redraws the portion of our UI displaying
    /// file contents. E.g. this is called when the current line
    /// changes.
    pub fn redraw_file(&mut self) {
        let current_line = self.current_line();
        self.print_file(current_line);
    }

    /// This function redraws the given line of the UI on the screen.
    pub fn redraw_line(&mut self, ui_line: usize) {
        let file_line = ui_line + self.current_line();
        self.print_line(ui_line, file_line);
    }

    pub fn print_line(&mut self, ui_line: usize, file_line: usize) {
        let contents = &self.editor.contents[file_line];
        let mut i = 0;
        for &b in contents {
            if b == b'\n' {
                continue;
            }
            self.term[(i, ui_line)].set_ch(b as char);
            i += 1;
        }
    }
}
<|endoftext|>


<commit_before>use crate::error::ErrorKind;
use crate::git::Git;
use std::env;

#[test]
fn git_get_current_branch() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let b = g.current_branch();
    assert!(b.is_ok(), "{:?}", b);
}

#[test]
fn git_get_commit_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let b = g.current_branch().unwrap();

    for spec in &["HEAD", &b] {
        match g.hash(spec) {
            Ok(h) => {
                for c in h.chars() {
                    assert!(c.is_ascii_hexdigit(), "{:?}", h);
                }
            }
            Err(err) => assert!(false, "{:?}", err),
        };
    }
}

#[test]
fn git_get_invalid_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g.hash("HEAD~114514").unwrap_err().kind() {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "commit"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_get_tag_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let h = g.tag_hash("0.10.1").unwrap();
    assert_eq!(h, "601e6c33bb760d8e7d5684a75eec1f0257b8ff22");
}

#[test]
fn git_get_invalid_tag_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g.tag_hash("this-tag-is-not-existing").unwrap_err().kind() {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "tag name"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_get_remote_url() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let u = g.remote_url("origin").unwrap();
    assert!(
        u.starts_with("git@") || u.starts_with("https://"),
        "invalid url: {:?}",
        u
    );
    assert!(u.contains("git-brws"), "invalid url: {:?}", u);
    // .git may not exist because GitHub allows omitting .git suffix in https Git URL
}

#[test]
fn git_get_invalid_remote_url() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g
        .remote_url("this-remote-is-not-existing")
        .unwrap_err()
        .kind()
    {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "remote"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_root_dir() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let p = g.root_dir().unwrap();
    assert!(p.join(".git").exists(), "{:?}", p);
}

#[test]
fn git_check_remote_contains() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let h = g.tag_hash("0.10.1").unwrap();
    let b = g.remote_contains(&h, "origin/master").unwrap();
    assert!(
        b,
        "Hash {} is not included in remote branch origin/master",
        &h
    );
}

#[test]
fn git_get_tracking_remote() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let (url, _remote_name) = g.tracking_remote_url(&Some("master")).unwrap();
    assert!(
        url.starts_with("https://") || url.starts_with("git@") || url.starts_with("ssh://"),
        "{:?}",
        url
    );
}

#[test]
fn unknown_branch_for_tracking_remote() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let err = g
        .tracking_remote_url(&Some("unknown-branch-this-is-not-existing"))
        .unwrap_err();
    assert!(matches!(err.kind(), ErrorKind::GitCommandError{ .. }));
}

#[test]
fn git_root_direcotry_not_found() {
    let mut root = env::current_dir().unwrap();
    while root.pop() {}
    let root = root;
    let g = Git::new(&root, "git");
    let err = g.root_dir().unwrap_err();
    assert!(matches!(err.kind(), ErrorKind::GitRootDirNotFound{ .. }));
}
<commit_msg>fix typo in test case name<commit_after>use crate::error::ErrorKind;
use crate::git::Git;
use std::env;

#[test]
fn git_get_current_branch() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let b = g.current_branch();
    assert!(b.is_ok(), "{:?}", b);
}

#[test]
fn git_get_commit_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let b = g.current_branch().unwrap();

    for spec in &["HEAD", &b] {
        match g.hash(spec) {
            Ok(h) => {
                for c in h.chars() {
                    assert!(c.is_ascii_hexdigit(), "{:?}", h);
                }
            }
            Err(err) => assert!(false, "{:?}", err),
        };
    }
}

#[test]
fn git_get_invalid_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g.hash("HEAD~114514").unwrap_err().kind() {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "commit"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_get_tag_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let h = g.tag_hash("0.10.1").unwrap();
    assert_eq!(h, "601e6c33bb760d8e7d5684a75eec1f0257b8ff22");
}

#[test]
fn git_get_invalid_tag_hash() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g.tag_hash("this-tag-is-not-existing").unwrap_err().kind() {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "tag name"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_get_remote_url() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let u = g.remote_url("origin").unwrap();
    assert!(
        u.starts_with("git@") || u.starts_with("https://"),
        "invalid url: {:?}",
        u
    );
    assert!(u.contains("git-brws"), "invalid url: {:?}", u);
    // .git may not exist because GitHub allows omitting .git suffix in https Git URL
}

#[test]
fn git_get_invalid_remote_url() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    match g
        .remote_url("this-remote-is-not-existing")
        .unwrap_err()
        .kind()
    {
        ErrorKind::GitObjectNotFound { kind, .. } => assert_eq!(*kind, "remote"),
        e => assert!(false, "Unexpected error: {:?}", e),
    }
}

#[test]
fn git_root_dir() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let p = g.root_dir().unwrap();
    assert!(p.join(".git").exists(), "{:?}", p);
}

#[test]
fn git_check_remote_contains() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let h = g.tag_hash("0.10.1").unwrap();
    let b = g.remote_contains(&h, "origin/master").unwrap();
    assert!(
        b,
        "Hash {} is not included in remote branch origin/master",
        &h
    );
}

#[test]
fn git_get_tracking_remote() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let (url, _remote_name) = g.tracking_remote_url(&Some("master")).unwrap();
    assert!(
        url.starts_with("https://") || url.starts_with("git@") || url.starts_with("ssh://"),
        "{:?}",
        url
    );
}

#[test]
fn unknown_branch_for_tracking_remote() {
    let cwd = env::current_dir().unwrap();
    let g = Git::new(&cwd, "git");
    let err = g
        .tracking_remote_url(&Some("unknown-branch-this-is-not-existing"))
        .unwrap_err();
    assert!(matches!(err.kind(), ErrorKind::GitCommandError{ .. }));
}

#[test]
fn git_root_directory_not_found() {
    let mut root = env::current_dir().unwrap();
    while root.pop() {}
    let root = root;
    let g = Git::new(&root, "git");
    let err = g.root_dir().unwrap_err();
    assert!(matches!(err.kind(), ErrorKind::GitRootDirNotFound{ .. }));
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
use std::sync::mpsc::Receiver;

use std::fmt;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            self.learn(message);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn string(&self) -> String {
        self.instances.iter()
            .map(|i| {
                let b = i.borrow();
                let b = b.word.borrow();
                b.name.clone()
            })
            .join(" ")
    }
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.string())
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<commit_msg>add public things<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::mpsc::Receiver;

use std::fmt;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
pub type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // Learn the message immediately
        self.learn(message);

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            self.learn(message);
        }
    }

    pub fn learn(&mut self, message: MessageCell) {
        // Vector of absolute perfect matches
        let mut vones = Vec::new();

        // Look through each word in the message
        for word in &message.borrow().instances {
            let borrow = word.borrow();
            // Check each instance in that words instances
            for instance in &borrow.word.borrow().instances {
                // Get the message for each instance
                let omessage = instance.borrow().message.clone();
                // Find what kind of matches exist between the messages
                if let Mismatch::One(best) =
                    Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                    vones.push(best);
                }
            }
        }

        // Now that we have perfect matches, merge them into the same Category
        for ms in vones {
            // We only want to combine if they aren't already in the same category
            if ms.0.borrow().category != ms.1.borrow().category {
                let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                Category::merge(cats);
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{} ~ {}", ib.word.borrow().name, &*ib.message.borrow());
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

pub struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn string(&self) -> String {
        self.instances.iter()
            .map(|i| {
                let b = i.borrow();
                let b = b.word.borrow();
                b.name.clone()
            })
            .join(" ")
    }
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

impl fmt::Display for Message {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.string())
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<|endoftext|>


<commit_before>use cgmath::prelude::*;
use cgmath::{Matrix4, Point2, Point3, Vector3};

use rand;

use crate::aabb::{self, AABB};
use crate::index_ptr::IndexPtr;
use crate::material::Material;
use crate::pt_renderer::{Intersect, Ray};
use crate::vertex::Vertex;
use crate::Float;

#[derive(Default)]
pub struct TriangleBuilder {
    vertices: Vec<IndexPtr<Vertex>>,
}

impl TriangleBuilder {
    pub fn new() -> Self {
        Self {
            vertices: Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: IndexPtr<Vertex>) {
        self.vertices.push(vertex);
    }

    pub fn build(self, material: IndexPtr<Material>) -> Result<Triangle, String> {
        if self.vertices.len() != 3 {
            Err("Triangle doesn't have 3 vertices!".to_string())
        } else {
            Ok(Triangle::new(
                self.vertices[0].clone(),
                self.vertices[1].clone(),
                self.vertices[2].clone(),
                material,
            ))
        }
    }
}

/// Tracable triangle
#[derive(Clone, Debug)]
pub struct Triangle {
    v1: IndexPtr<Vertex>,
    v2: IndexPtr<Vertex>,
    v3: IndexPtr<Vertex>,
    to_barycentric: Matrix4<Float>,
    pub material: IndexPtr<Material>,
}

impl Triangle {
    fn new(
        v1: IndexPtr<Vertex>,
        v2: IndexPtr<Vertex>,
        v3: IndexPtr<Vertex>,
        material: IndexPtr<Material>,
    ) -> Self {
        let p1 = v1.p;
        let p2 = v2.p;
        let p3 = v3.p;
        let z = (p2 - p1).cross(p3 - p1).normalize();
        let from_barycentric = Matrix4::from_cols(
            (p2 - p1).extend(0.0),
            (p3 - p1).extend(0.0),
            z.extend(0.0),
            p1.to_homogeneous(),
        );
        let to_barycentric = from_barycentric
            .invert()
            .expect("Non invertible barycentric tranform");
        Self {
            v1,
            v2,
            v3,
            to_barycentric,
            material,
        }
    }

    fn normal(&self, u: Float, v: Float) -> Vector3<Float> {
        let n1 = self.v1.n;
        let n2 = self.v2.n;
        let n3 = self.v3.n;
        (1.0 - u - v) * n1 + u * n2 + v * n3
    }

    fn tex_coords(&self, u: Float, v: Float) -> Point2<Float> {
        let t1 = self.v1.t;
        let t2 = self.v2.t;
        let t3 = self.v3.t;
        (1.0 - u - v) * t1 + (u * t2 - (-v) * t3)
    }

    pub fn aabb(&self) -> AABB {
        let mut min = self.v1.p;
        min = aabb::min_point(&min, &self.v2.p);
        min = aabb::min_point(&min, &self.v3.p);
        let mut max = self.v1.p;
        max = aabb::max_point(&max, &self.v2.p);
        max = aabb::max_point(&max, &self.v3.p);
        AABB { min, max }
    }

    pub fn center(&self) -> Point3<Float> {
        Point3::centroid(&[self.v1.p, self.v2.p, self.v3.p])
    }

    pub fn area(&self) -> Float {
        0.5 / self.to_barycentric.determinant().abs()
    }

    pub fn random_point(&self) -> (Point3<Float>, Vector3<Float>) {
        let mut u: Float = rand::random();
        let mut v: Float = rand::random();
        // TODO: use sampling that doesnt need this flip
        if u + v > 1.0 {
            u = 1.0 - u;
            v = 1.0 - v;
        }
        (self.pos(u, v), self.normal(u, v))
    }

    fn pos(&self, u: Float, v: Float) -> Point3<Float> {
        // Have to substract one component since cgmath points cannot by summed
        // and there is not a cleaner method to convert to Vector3
        (1.0 - u - v) * self.v1.p + (u * self.v2.p - (-v) * self.v3.p)
    }
}

#[derive(Debug)]
pub struct Hit<'a> {
    pub tri: &'a Triangle,
    pub t: Float,
    pub u: Float,
    pub v: Float,
}

impl<'a> Intersect<'a, Hit<'a>> for Triangle {
    fn intersect(&self, ray: &Ray) -> Option<Hit> {
        let bary_o = self.to_barycentric * ray.orig.to_homogeneous();
        let bary_d = self.to_barycentric * ray.dir.extend(0.0);
        let t = -bary_o.z / bary_d.z;
        let u = bary_o.x + t * bary_d.x;
        let v = bary_o.y + t * bary_d.y;
        if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 && t < ray.length {
            Some(Hit { tri: self, t, u, v })
        } else {
            None
        }
    }
}

impl Hit<'_> {
    pub fn pos(&self) -> Point3<Float> {
        self.tri.pos(self.u, self.v)
    }

    pub fn normal(&self) -> Vector3<Float> {
        self.tri.normal(self.u, self.v)
    }

    pub fn tex_coords(&self) -> Point2<Float> {
        self.tri.tex_coords(self.u, self.v)
    }
}
<commit_msg>Improve triangle sampling<commit_after>use cgmath::prelude::*;
use cgmath::{Matrix4, Point2, Point3, Vector3};

use rand;

use crate::aabb::{self, AABB};
use crate::index_ptr::IndexPtr;
use crate::material::Material;
use crate::pt_renderer::{Intersect, Ray};
use crate::vertex::Vertex;
use crate::Float;

#[derive(Default)]
pub struct TriangleBuilder {
    vertices: Vec<IndexPtr<Vertex>>,
}

impl TriangleBuilder {
    pub fn new() -> Self {
        Self {
            vertices: Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: IndexPtr<Vertex>) {
        self.vertices.push(vertex);
    }

    pub fn build(self, material: IndexPtr<Material>) -> Result<Triangle, String> {
        if self.vertices.len() != 3 {
            Err("Triangle doesn't have 3 vertices!".to_string())
        } else {
            Ok(Triangle::new(
                self.vertices[0].clone(),
                self.vertices[1].clone(),
                self.vertices[2].clone(),
                material,
            ))
        }
    }
}

/// Tracable triangle
#[derive(Clone, Debug)]
pub struct Triangle {
    v1: IndexPtr<Vertex>,
    v2: IndexPtr<Vertex>,
    v3: IndexPtr<Vertex>,
    to_barycentric: Matrix4<Float>,
    pub material: IndexPtr<Material>,
}

impl Triangle {
    fn new(
        v1: IndexPtr<Vertex>,
        v2: IndexPtr<Vertex>,
        v3: IndexPtr<Vertex>,
        material: IndexPtr<Material>,
    ) -> Self {
        let p1 = v1.p;
        let p2 = v2.p;
        let p3 = v3.p;
        let z = (p2 - p1).cross(p3 - p1).normalize();
        let from_barycentric = Matrix4::from_cols(
            (p2 - p1).extend(0.0),
            (p3 - p1).extend(0.0),
            z.extend(0.0),
            p1.to_homogeneous(),
        );
        let to_barycentric = from_barycentric
            .invert()
            .expect("Non invertible barycentric tranform");
        Self {
            v1,
            v2,
            v3,
            to_barycentric,
            material,
        }
    }

    fn normal(&self, u: Float, v: Float) -> Vector3<Float> {
        let n1 = self.v1.n;
        let n2 = self.v2.n;
        let n3 = self.v3.n;
        (1.0 - u - v) * n1 + u * n2 + v * n3
    }

    fn tex_coords(&self, u: Float, v: Float) -> Point2<Float> {
        let t1 = self.v1.t;
        let t2 = self.v2.t;
        let t3 = self.v3.t;
        (1.0 - u - v) * t1 + (u * t2 - (-v) * t3)
    }

    pub fn aabb(&self) -> AABB {
        let mut min = self.v1.p;
        min = aabb::min_point(&min, &self.v2.p);
        min = aabb::min_point(&min, &self.v3.p);
        let mut max = self.v1.p;
        max = aabb::max_point(&max, &self.v2.p);
        max = aabb::max_point(&max, &self.v3.p);
        AABB { min, max }
    }

    pub fn center(&self) -> Point3<Float> {
        Point3::centroid(&[self.v1.p, self.v2.p, self.v3.p])
    }

    pub fn area(&self) -> Float {
        0.5 / self.to_barycentric.determinant().abs()
    }

    pub fn random_point(&self) -> (Point3<Float>, Vector3<Float>) {
        let r1: Float = rand::random();
        let r2: Float = rand::random();
        let sr1 = r1.sqrt();
        let u = 1.0 - sr1;
        let v = r2 * sr1;
        (self.pos(u, v), self.normal(u, v))
    }

    fn pos(&self, u: Float, v: Float) -> Point3<Float> {
        // Have to substract one component since cgmath points cannot by summed
        // and there is not a cleaner method to convert to Vector3
        (1.0 - u - v) * self.v1.p + (u * self.v2.p - (-v) * self.v3.p)
    }
}

#[derive(Debug)]
pub struct Hit<'a> {
    pub tri: &'a Triangle,
    pub t: Float,
    pub u: Float,
    pub v: Float,
}

impl<'a> Intersect<'a, Hit<'a>> for Triangle {
    fn intersect(&self, ray: &Ray) -> Option<Hit> {
        let bary_o = self.to_barycentric * ray.orig.to_homogeneous();
        let bary_d = self.to_barycentric * ray.dir.extend(0.0);
        let t = -bary_o.z / bary_d.z;
        let u = bary_o.x + t * bary_d.x;
        let v = bary_o.y + t * bary_d.y;
        if u >= 0.0 && v >= 0.0 && u + v <= 1.0 && t > 0.0 && t < ray.length {
            Some(Hit { tri: self, t, u, v })
        } else {
            None
        }
    }
}

impl Hit<'_> {
    pub fn pos(&self) -> Point3<Float> {
        self.tri.pos(self.u, self.v)
    }

    pub fn normal(&self) -> Vector3<Float> {
        self.tri.normal(self.u, self.v)
    }

    pub fn tex_coords(&self) -> Point2<Float> {
        self.tri.tex_coords(self.u, self.v)
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.
use std::io::timer;
use std::comm::channel;
use std::mem::{transmute, transmute_copy};
use time::precise_time_ns;
use std::time::duration::Duration;

use sdl2::event::poll_event;
use sdl2::event::Event;
use sdl2::event::Event::*;

use gfx::GameDisplay;

pub mod prefab;

pub fn throttle(fps: uint, cb: || -> bool) {
    let target_fps = (1000 / fps) as u64;
    loop {
        let next_frame = (precise_time_ns() / 1000000) + target_fps;
        match cb() {
            false => break,
            _ => {}
        }
        let now_time = precise_time_ns() / 1000000;
        if  now_time < next_frame {
            let sleep_gap = next_frame - now_time;
            timer::sleep(Duration::milliseconds(sleep_gap as i64));
        }
    }
}

pub trait DisplayViewContext {
    fn get_display<'a>(&'a self) -> &'a GameDisplay;
}

// ViewManager API exploration
pub trait ActiveView<TViewCtx: DisplayViewContext, TOut: Send> : PassiveView<TViewCtx> {
    fn active_update<'a>(&'a mut self, ctx: &TViewCtx, events: &[Event], ms_time: u64,
                         passives: &mut Vec<& mut PassiveView<TViewCtx> >)
                         -> Option<TOut>;
    fn yield_to<'a, TOut: Send, TActive: ActiveView<TViewCtx, TOut>>(&'a mut self, ctx: &TViewCtx,
                                active: &mut TActive,
                                passives: &mut Vec<&mut PassiveView<TViewCtx>>) -> TOut {
        let (sender, receiver) = channel();
        let mut cont = true;
        while cont {
            let time = precise_time_ns() / 1000000;
            let mut events = Vec::new();
            {
                for view in passives.iter_mut() {
                    view.passive_update(ctx, time);
                }
            }
            self.passive_update(ctx, time);
            active.passive_update(ctx, time);
            loop {
                match poll_event() {
                    Event::None => { break; },
                    event => { events.push(event); }
                }
            }
            let result = active.active_update(ctx, events.as_slice(), time, passives);
            ctx.get_display().renderer.present();
            if result.is_some() {
                cont = false;
                sender.send(result.expect("definitely gonna be something!"));
            }
        }
        receiver.recv()
    }
}

pub trait PassiveView<TViewCtx: DisplayViewContext> {
    fn passive_update(&mut self, ctx: &TViewCtx, ms_time: u64);
}

pub struct PassiveBox<'a, TActive: 'a> {
    inner: &'a mut TActive
}

impl<'a, TViewCtx: DisplayViewContext, TOut: Send, TActive: ActiveView<TViewCtx, TOut>> PassiveBox<'a, TActive> {
    pub fn new(item: &'a mut TActive) -> PassiveBox<'a, TActive> {
        PassiveBox { inner: item }
    }
}

impl<'a, TViewCtx: DisplayViewContext, TOut: Send, TActive: ActiveView<TViewCtx, TOut>> PassiveView<TViewCtx> for PassiveBox<'a, TActive> {
    fn passive_update(&mut self, ctx: &TViewCtx, ms_time: u64) {
        self.inner.passive_update(ctx, ms_time);
    }
}
<commit_msg>more shuffling around in active/passive view.. will have to be replaced<commit_after>// Copyright 2013-2014 Jeffery Olson
//
// Licensed under the 3-Clause BSD License, see LICENSE.txt
// at the top-level of this repository.
// This file may not be copied, modified, or distributed
// except according to those terms.
use std::io::timer;
use std::comm::channel;
use std::mem::{transmute, transmute_copy};
use time::precise_time_ns;
use std::time::duration::Duration;

use sdl2::event::poll_event;
use sdl2::event::Event;
use sdl2::event::Event::*;

use gfx::GameDisplay;

pub mod prefab;

pub fn throttle(fps: uint, cb: || -> bool) {
    let target_fps = (1000 / fps) as u64;
    loop {
        let next_frame = (precise_time_ns() / 1000000) + target_fps;
        match cb() {
            false => break,
            _ => {}
        }
        let now_time = precise_time_ns() / 1000000;
        if  now_time < next_frame {
            let sleep_gap = next_frame - now_time;
            timer::sleep(Duration::milliseconds(sleep_gap as i64));
        }
    }
}

pub trait DisplayViewContext {
    fn get_display<'a>(&'a self) -> &'a GameDisplay;
}

// ViewManager API exploration
pub trait ActiveView<TViewCtx: DisplayViewContext, TOut: Send> : PassiveView<TViewCtx> {
    fn active_update<'a>(&'a mut self, ctx: &TViewCtx, events: &[Event], ms_time: u64,
                         passives: &mut Vec<& mut PassiveView<TViewCtx> >)
                         -> Option<TOut>;
    fn yield_to<'a, TOtherOut: Send, TActive: ActiveView<TViewCtx, TOtherOut>>(&'a mut self, ctx: &TViewCtx,
                                active: &mut TActive,
                                passives: &mut Vec<&mut PassiveView<TViewCtx>>) -> TOtherOut {
        let (sender, receiver) = channel();
        let mut cont = true;
        while cont {
            let time = precise_time_ns() / 1000000;
            let mut events = Vec::new();
            let result = {
                {
                    for view in passives.iter_mut() {
                        println!("before passive update");
                        view.passive_update(ctx, time);
                        println!("after passive update");
                    }
                }
                active.passive_update(ctx, time);
                loop {
                    match poll_event() {
                        Event::None => { break; },
                        event => { events.push(event); }
                    }
                }
                let result = active.active_update(ctx, events.as_slice(), time, passives);
                ctx.get_display().renderer.present();
                result
            };
            if result.is_some() {
                cont = false;
                sender.send(result.expect("definitely gonna be something!"));
            }
        }
        receiver.recv()
    }
}

pub trait PassiveView<TViewCtx: DisplayViewContext> {
    fn passive_update(&mut self, ctx: &TViewCtx, ms_time: u64);
}

pub struct PassiveBox<'a, TActive: 'a> {
    inner: &'a mut TActive
}

impl<'a, TViewCtx: DisplayViewContext, TOut: Send, TActive: ActiveView<TViewCtx, TOut>> PassiveBox<'a, TActive> {
    pub fn new(item: &'a mut TActive) -> PassiveBox<'a, TActive> {
        PassiveBox { inner: item }
    }
}

impl<'a, TViewCtx: DisplayViewContext, TOut: Send, TActive: ActiveView<TViewCtx, TOut>> PassiveView<TViewCtx> for PassiveBox<'a, TActive> {
    fn passive_update(&mut self, ctx: &TViewCtx, ms_time: u64) {
        self.inner.passive_update(ctx, ms_time);
    }
}
<|endoftext|>


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

mod board;
mod board_parts;
mod sys;

use imgui::{ImGuiCond, ImStr, ImVec2, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                insert_if_empty(&mut event, Some(Event::NewGame));
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            insert_if_empty(
                &mut event,
                board(model, Vec2::new(size.0 - 16.0, size.1 - 135.0)),
            );

            if model.white_pieces == 0 {
                ui.text("Black wins!");
            } else if model.black_pieces == 0 {
                ui.text("White wins!");
            } else {
                match model.turn {
                    Color::White => ui.text("It's white's turn."),
                    Color::Black => ui.text("It's black's turn."),
                }

                ui.text(format!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces, model.white_hexes,
                ));
                ui.text(format!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces, model.black_hexes,
                ));
            }

            if disableable_button(
                ui,
                im_str!("Exchange"),
                Vec2::new(100.0, 20.0),
                !model.can_exchange()
            ) {
                insert_if_empty(&mut event, Some(Event::Exchange));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}

fn insert_if_empty<T>(a: &mut Option<T>, b: Option<T>) {
    if a.is_none() {
        *a = b;
    }
}

// Fake a disabled button by ignoring click events, since it's not actually implemented yet
fn disableable_button<'p, S: Into<ImVec2>>(
    ui: &Ui,
    label: &'p ImStr,
    size: S,
    disabled: bool,
) -> bool {
    if disabled {
        unsafe {
            imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::Alpha, 0.5);
            ui.button(label, size);
            imgui_sys::igPopStyleVar(1);
        }
        false
    } else {
        ui.button(label, size)
    }
}
<commit_msg>Hide exchange button instead of "disabling" it<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

mod board;
mod board_parts;
mod sys;

use imgui::{ImGuiCond, Ui};
use imgui_sys;

use model::{Color, FieldCoord, Model};
use vec2::Vec2;
use self::board::board;
pub use self::sys::run;

pub enum Event {
    Click(FieldCoord),
    Exchange,
    NewGame,
}

pub fn draw(ui: &Ui, size: (f32, f32), model: &Model) -> Option<Event> {
    unsafe {
        imgui_sys::igPushStyleVar(imgui_sys::ImGuiStyleVar::WindowRounding, 0.0);
    }

    let mut event = None;

    ui.main_menu_bar(|| {
        ui.menu(im_str!("Game")).build(|| {
            if ui.menu_item(im_str!("New game")).build() {
                insert_if_empty(&mut event, Some(Event::NewGame));
            }
        });
    });

    ui.window(im_str!("Coerceo"))
        .size(size, ImGuiCond::Always)
        .position((0.0, 19.0), ImGuiCond::Once)
        .title_bar(false)
        .resizable(false)
        .movable(false)
        .build(|| {
            ui.text("Welcome to Coerceo!");

            insert_if_empty(
                &mut event,
                board(model, Vec2::new(size.0 - 16.0, size.1 - 135.0)),
            );

            if model.white_pieces == 0 {
                ui.text("Black wins!");
            } else if model.black_pieces == 0 {
                ui.text("White wins!");
            } else {
                match model.turn {
                    Color::White => ui.text("It's white's turn."),
                    Color::Black => ui.text("It's black's turn."),
                }

                ui.text(format!(
                    "White has {} piece(s) left and {} captured hex(es).",
                    model.white_pieces, model.white_hexes,
                ));
                ui.text(format!(
                    "Black has {} piece(s) left and {} captured hex(es).",
                    model.black_pieces, model.black_hexes,
                ));
            }

            if model.can_exchange() && ui.button(im_str!("Exchange"), Vec2::new(100.0, 20.0)) {
                insert_if_empty(&mut event, Some(Event::Exchange));
            }
        });

    unsafe {
        imgui_sys::igPopStyleVar(1);
    }

    event
}

fn insert_if_empty<T>(a: &mut Option<T>, b: Option<T>) {
    if a.is_none() {
        *a = b;
    }
}
<|endoftext|>


<commit_before>extern crate zip;

use std::fs;

fn info_for_archive_item(archive_item: zip::read::ZipFile) -> String {
    let info = String::new();
    let item_path = archive_item.name();
    format!("{}\n\t{}", info, item_path)
}

fn info_for_path(path: String) -> String {
    let mut info = String::new();
    info = format!("{}", path);

    // Add archive contents:
    let file = fs::File::open(&path).unwrap();
    let mut whole_archive = zip::ZipArchive::new(file).unwrap();

    for i in 0..whole_archive.len() {
        let archive_item = whole_archive.by_index(i).unwrap();
        info = format!("{}{}", info, info_for_archive_item(archive_item));
    }

    info
}

pub fn display_info_for_paths(paths: Vec<String>) {
    for path in paths {
        println!("{}", info_for_path(path));
    }
}

#[cfg(test)]
mod test {
    // Not sure how to test this yet...
    // Mostly integration-test-oriented?
}
<commit_msg>Add support for greater archive detail<commit_after>extern crate zip;

use std::fs;

fn info_for_archive_item(archive_item: zip::read::ZipFile) -> String {
    let mut info = String::new();
    let item_path = archive_item.name();
    info = format!("{}\n\t{}", info, item_path);

    let compression_type = archive_item.compression();
    info = format!("{}\n\t\tCompression type: {}", info, compression_type);

    let original_size = archive_item.size();
    info = format!("{}\n\t\tOriginal size: {}", info, original_size);

    let compressed_size = archive_item.compressed_size();
    info = format!("{}\n\t\tCompressed size: {}", info, compressed_size);

    if original_size > 0 {
        let compression_rate =
            (original_size as f64 - compressed_size as f64)
            / original_size as f64;
        let compression_perc = format!("{:.*}%", 2, compression_rate * 100.0);
        info = format!("{}\n\t\tCompression_rate: {}", info, compression_perc);
    }

    let comment = archive_item.comment();
    if comment.len() > 0 {
        info = format!("{}\n\t\tComment: {}", info, comment);
    }

    info
}

fn info_for_path(path: String) -> String {
    let mut info = String::new();
    info = format!("{}", path);

    // Add archive contents:
    let file = fs::File::open(&path).unwrap();
    let mut whole_archive = zip::ZipArchive::new(file).unwrap();

    for i in 0..whole_archive.len() {
        let archive_item = whole_archive.by_index(i).unwrap();
        info = format!("{}{}", info, info_for_archive_item(archive_item));
    }

    info
}

pub fn display_info_for_paths(paths: Vec<String>) {
    for path in paths {
        println!("{}", info_for_path(path));
    }
}

#[cfg(test)]
mod test {
    // Not sure how to test this yet...
    // Mostly integration-test-oriented?
}
<|endoftext|>


<commit_before>use crate::{
    executor::Executor,
    message::{Delivery, DeliveryResult},
    types::ShortString,
    BasicProperties, Error, Result,
};
use crossbeam_channel::{Receiver, Sender};
use futures_core::stream::Stream;
use log::trace;
use parking_lot::{Mutex, MutexGuard};
use std::{
    fmt,
    future::Future,
    pin::Pin,
    sync::Arc,
    task::{Context, Poll, Waker},
};

pub trait ConsumerDelegate: Send + Sync {
    fn on_new_delivery(&self, delivery: DeliveryResult)
        -> Pin<Box<dyn Future<Output = ()> + Send>>;
    fn drop_prefetched_messages(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(async move {})
    }
}

impl<
        F: Future<Output = ()> + Send + 'static,
        DeliveryHandler: Fn(DeliveryResult) -> F + Send + Sync + 'static,
    > ConsumerDelegate for DeliveryHandler
{
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(self(delivery))
    }
}

#[derive(Clone)]
pub struct Consumer {
    inner: Arc<Mutex<ConsumerInner>>,
}

impl Consumer {
    pub(crate) fn new(consumer_tag: ShortString, executor: Arc<dyn Executor>) -> Consumer {
        Consumer {
            inner: Arc::new(Mutex::new(ConsumerInner::new(consumer_tag, executor))),
        }
    }

    pub fn inner(&self) -> MutexGuard<'_, ConsumerInner> {
        self.inner.lock()
    }

    pub fn set_delegate<D: ConsumerDelegate + 'static>(&self, delegate: D) {
        let mut inner = self.inner();
        while let Some(delivery) = inner.next_delivery() {
            delegate.on_new_delivery(delivery);
        }
        inner.delegate = Some(Arc::new(Box::new(delegate)));
    }

    pub(crate) fn start_new_delivery(&mut self, delivery: Delivery) {
        self.inner().current_message = Some(delivery)
    }

    pub(crate) fn set_delivery_properties(&mut self, properties: BasicProperties) {
        if let Some(delivery) = self.inner().current_message.as_mut() {
            delivery.properties = properties;
        }
    }

    pub(crate) fn receive_delivery_content(&mut self, payload: Vec<u8>) {
        if let Some(delivery) = self.inner().current_message.as_mut() {
            delivery.receive_content(payload);
        }
    }

    pub(crate) fn new_delivery_complete(&mut self) -> Result<()> {
        let mut inner = self.inner();
        if let Some(delivery) = inner.current_message.take() {
            inner.new_delivery(delivery)?;
        }
        Ok(())
    }

    pub(crate) fn drop_prefetched_messages(&self) -> Result<()> {
        self.inner().drop_prefetched_messages()
    }

    pub(crate) fn cancel(&self) -> Result<()> {
        self.inner().cancel()
    }

    pub(crate) fn set_error(&self, error: Error) -> Result<()> {
        self.inner().set_error(error)
    }
}

pub struct ConsumerInner {
    current_message: Option<Delivery>,
    deliveries_in: Sender<DeliveryResult>,
    deliveries_out: Receiver<DeliveryResult>,
    task: Option<Waker>,
    tag: ShortString,
    delegate: Option<Arc<Box<dyn ConsumerDelegate>>>,
    executor: Arc<dyn Executor>,
}

pub struct ConsumerIterator {
    receiver: Receiver<DeliveryResult>,
}

impl Iterator for ConsumerIterator {
    type Item = Result<Delivery>;

    fn next(&mut self) -> Option<Self::Item> {
        self.receiver.recv().ok().and_then(Result::transpose)
    }
}

impl IntoIterator for Consumer {
    type Item = Result<Delivery>;
    type IntoIter = ConsumerIterator;

    fn into_iter(self) -> Self::IntoIter {
        ConsumerIterator {
            receiver: self.inner().deliveries_out.clone(),
        }
    }
}

impl fmt::Debug for Consumer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Consumer");
        if let Some(inner) = self.inner.try_lock() {
            debug
                .field("tag", &inner.tag)
                .field("executor", &inner.executor)
                .field("task", &inner.task);
        }
        debug.finish()
    }
}

impl ConsumerInner {
    fn new(consumer_tag: ShortString, executor: Arc<dyn Executor>) -> Self {
        let (sender, receiver) = crossbeam_channel::unbounded();
        Self {
            current_message: None,
            deliveries_in: sender,
            deliveries_out: receiver,
            task: None,
            tag: consumer_tag,
            delegate: None,
            executor,
        }
    }

    pub fn next_delivery(&mut self) -> Option<DeliveryResult> {
        self.deliveries_out.try_recv().ok()
    }

    pub fn tag(&self) -> &ShortString {
        &self.tag
    }

    fn new_delivery(&mut self, delivery: Delivery) -> Result<()> {
        trace!("new_delivery; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor
                .spawn(delegate.on_new_delivery(Ok(Some(delivery))))?;
        } else {
            self.deliveries_in
                .send(Ok(Some(delivery)))
                .expect("failed to send delivery to consumer");
        }
        if let Some(task) = self.task.as_ref() {
            task.wake_by_ref();
        }
        Ok(())
    }

    fn drop_prefetched_messages(&mut self) -> Result<()> {
        trace!("drop_prefetched_messages; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.drop_prefetched_messages())?;
        }
        while let Some(_) = self.next_delivery() {}
        Ok(())
    }

    fn cancel(&mut self) -> Result<()> {
        trace!("cancel; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.on_new_delivery(Ok(None)))?;
        } else {
            self.deliveries_in
                .send(Ok(None))
                .expect("failed to send cancel to consumer");
        }
        if let Some(task) = self.task.take() {
            task.wake();
        }
        Ok(())
    }

    pub fn set_error(&mut self, error: Error) -> Result<()> {
        trace!("set_error; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.on_new_delivery(Err(error)))?;
        } else {
            self.deliveries_in
                .send(Err(error))
                .expect("failed to send error to consumer");
        }
        self.cancel()
    }
}

impl Stream for Consumer {
    type Item = Result<Delivery>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        trace!("consumer poll; polling transport");
        let mut inner = self.inner();
        trace!(
            "consumer poll; acquired inner lock, consumer_tag={}",
            inner.tag()
        );
        inner.task = Some(cx.waker().clone());
        if let Some(delivery) = inner.next_delivery() {
            match delivery {
                Ok(Some(delivery)) => {
                    trace!(
                        "delivery; consumer_tag={}, delivery_tag={:?}",
                        inner.tag(),
                        delivery.delivery_tag
                    );
                    Poll::Ready(Some(Ok(delivery)))
                }
                Ok(None) => {
                    trace!("consumer canceled; consumer_tag={}", inner.tag());
                    Poll::Ready(None)
                }
                Err(error) => Poll::Ready(Some(Err(error))),
            }
        } else {
            trace!("delivery; status=NotReady, consumer_tag={}", inner.tag());
            Poll::Pending
        }
    }
}

#[cfg(test)]
mod futures_tests {
    use super::*;
    use crate::executor::DefaultExecutor;

    use std::task::{Context, Poll};

    use futures_test::task::new_count_waker;
    use futures_util::stream::StreamExt;

    #[test]
    fn stream_on_cancel() {
        let (waker, awoken_count) = new_count_waker();
        let mut cx = Context::from_waker(&waker);

        let mut consumer = Consumer::new(
            ShortString::from("test-consumer"),
            Arc::new(DefaultExecutor::default()),
        );

        assert_eq!(awoken_count.get(), 0);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Pending);

        consumer.cancel().unwrap();

        assert_eq!(awoken_count.get(), 1);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Ready(None));
    }

    #[test]
    fn stream_on_error() {
        let (waker, awoken_count) = new_count_waker();
        let mut cx = Context::from_waker(&waker);

        let mut consumer = Consumer::new(
            ShortString::from("test-consumer"),
            Arc::new(DefaultExecutor::default()),
        );

        assert_eq!(awoken_count.get(), 0);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Pending);

        consumer.set_error(Error::InvalidFrameReceived).unwrap();

        assert_eq!(awoken_count.get(), 1);
        assert_eq!(
            consumer.poll_next_unpin(&mut cx),
            Poll::Ready(Some(Err(Error::InvalidFrameReceived)))
        );
    }
}
<commit_msg>consumer: update deprecated log<commit_after>use crate::{
    executor::Executor,
    message::{Delivery, DeliveryResult},
    types::ShortString,
    BasicProperties, Error, Result,
};
use crossbeam_channel::{Receiver, Sender};
use futures_core::stream::Stream;
use log::trace;
use parking_lot::{Mutex, MutexGuard};
use std::{
    fmt,
    future::Future,
    pin::Pin,
    sync::Arc,
    task::{Context, Poll, Waker},
};

pub trait ConsumerDelegate: Send + Sync {
    fn on_new_delivery(&self, delivery: DeliveryResult)
        -> Pin<Box<dyn Future<Output = ()> + Send>>;
    fn drop_prefetched_messages(&self) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(async move {})
    }
}

impl<
        F: Future<Output = ()> + Send + 'static,
        DeliveryHandler: Fn(DeliveryResult) -> F + Send + Sync + 'static,
    > ConsumerDelegate for DeliveryHandler
{
    fn on_new_delivery(
        &self,
        delivery: DeliveryResult,
    ) -> Pin<Box<dyn Future<Output = ()> + Send>> {
        Box::pin(self(delivery))
    }
}

#[derive(Clone)]
pub struct Consumer {
    inner: Arc<Mutex<ConsumerInner>>,
}

impl Consumer {
    pub(crate) fn new(consumer_tag: ShortString, executor: Arc<dyn Executor>) -> Consumer {
        Consumer {
            inner: Arc::new(Mutex::new(ConsumerInner::new(consumer_tag, executor))),
        }
    }

    pub fn inner(&self) -> MutexGuard<'_, ConsumerInner> {
        self.inner.lock()
    }

    pub fn set_delegate<D: ConsumerDelegate + 'static>(&self, delegate: D) {
        let mut inner = self.inner();
        while let Some(delivery) = inner.next_delivery() {
            delegate.on_new_delivery(delivery);
        }
        inner.delegate = Some(Arc::new(Box::new(delegate)));
    }

    pub(crate) fn start_new_delivery(&mut self, delivery: Delivery) {
        self.inner().current_message = Some(delivery)
    }

    pub(crate) fn set_delivery_properties(&mut self, properties: BasicProperties) {
        if let Some(delivery) = self.inner().current_message.as_mut() {
            delivery.properties = properties;
        }
    }

    pub(crate) fn receive_delivery_content(&mut self, payload: Vec<u8>) {
        if let Some(delivery) = self.inner().current_message.as_mut() {
            delivery.receive_content(payload);
        }
    }

    pub(crate) fn new_delivery_complete(&mut self) -> Result<()> {
        let mut inner = self.inner();
        if let Some(delivery) = inner.current_message.take() {
            inner.new_delivery(delivery)?;
        }
        Ok(())
    }

    pub(crate) fn drop_prefetched_messages(&self) -> Result<()> {
        self.inner().drop_prefetched_messages()
    }

    pub(crate) fn cancel(&self) -> Result<()> {
        self.inner().cancel()
    }

    pub(crate) fn set_error(&self, error: Error) -> Result<()> {
        self.inner().set_error(error)
    }
}

pub struct ConsumerInner {
    current_message: Option<Delivery>,
    deliveries_in: Sender<DeliveryResult>,
    deliveries_out: Receiver<DeliveryResult>,
    task: Option<Waker>,
    tag: ShortString,
    delegate: Option<Arc<Box<dyn ConsumerDelegate>>>,
    executor: Arc<dyn Executor>,
}

pub struct ConsumerIterator {
    receiver: Receiver<DeliveryResult>,
}

impl Iterator for ConsumerIterator {
    type Item = Result<Delivery>;

    fn next(&mut self) -> Option<Self::Item> {
        self.receiver.recv().ok().and_then(Result::transpose)
    }
}

impl IntoIterator for Consumer {
    type Item = Result<Delivery>;
    type IntoIter = ConsumerIterator;

    fn into_iter(self) -> Self::IntoIter {
        ConsumerIterator {
            receiver: self.inner().deliveries_out.clone(),
        }
    }
}

impl fmt::Debug for Consumer {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut debug = f.debug_struct("Consumer");
        if let Some(inner) = self.inner.try_lock() {
            debug
                .field("tag", &inner.tag)
                .field("executor", &inner.executor)
                .field("task", &inner.task);
        }
        debug.finish()
    }
}

impl ConsumerInner {
    fn new(consumer_tag: ShortString, executor: Arc<dyn Executor>) -> Self {
        let (sender, receiver) = crossbeam_channel::unbounded();
        Self {
            current_message: None,
            deliveries_in: sender,
            deliveries_out: receiver,
            task: None,
            tag: consumer_tag,
            delegate: None,
            executor,
        }
    }

    pub fn next_delivery(&mut self) -> Option<DeliveryResult> {
        self.deliveries_out.try_recv().ok()
    }

    pub fn tag(&self) -> &ShortString {
        &self.tag
    }

    fn new_delivery(&mut self, delivery: Delivery) -> Result<()> {
        trace!("new_delivery; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor
                .spawn(delegate.on_new_delivery(Ok(Some(delivery))))?;
        } else {
            self.deliveries_in
                .send(Ok(Some(delivery)))
                .expect("failed to send delivery to consumer");
        }
        if let Some(task) = self.task.as_ref() {
            task.wake_by_ref();
        }
        Ok(())
    }

    fn drop_prefetched_messages(&mut self) -> Result<()> {
        trace!("drop_prefetched_messages; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.drop_prefetched_messages())?;
        }
        while let Some(_) = self.next_delivery() {}
        Ok(())
    }

    fn cancel(&mut self) -> Result<()> {
        trace!("cancel; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.on_new_delivery(Ok(None)))?;
        } else {
            self.deliveries_in
                .send(Ok(None))
                .expect("failed to send cancel to consumer");
        }
        if let Some(task) = self.task.take() {
            task.wake();
        }
        Ok(())
    }

    pub fn set_error(&mut self, error: Error) -> Result<()> {
        trace!("set_error; consumer_tag={}", self.tag);
        if let Some(delegate) = self.delegate.as_ref() {
            let delegate = delegate.clone();
            self.executor.spawn(delegate.on_new_delivery(Err(error)))?;
        } else {
            self.deliveries_in
                .send(Err(error))
                .expect("failed to send error to consumer");
        }
        self.cancel()
    }
}

impl Stream for Consumer {
    type Item = Result<Delivery>;

    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        trace!("consumer poll_next");
        let mut inner = self.inner();
        trace!(
            "consumer poll; acquired inner lock, consumer_tag={}",
            inner.tag()
        );
        inner.task = Some(cx.waker().clone());
        if let Some(delivery) = inner.next_delivery() {
            match delivery {
                Ok(Some(delivery)) => {
                    trace!(
                        "delivery; consumer_tag={}, delivery_tag={:?}",
                        inner.tag(),
                        delivery.delivery_tag
                    );
                    Poll::Ready(Some(Ok(delivery)))
                }
                Ok(None) => {
                    trace!("consumer canceled; consumer_tag={}", inner.tag());
                    Poll::Ready(None)
                }
                Err(error) => Poll::Ready(Some(Err(error))),
            }
        } else {
            trace!("delivery; status=NotReady, consumer_tag={}", inner.tag());
            Poll::Pending
        }
    }
}

#[cfg(test)]
mod futures_tests {
    use super::*;
    use crate::executor::DefaultExecutor;

    use std::task::{Context, Poll};

    use futures_test::task::new_count_waker;
    use futures_util::stream::StreamExt;

    #[test]
    fn stream_on_cancel() {
        let (waker, awoken_count) = new_count_waker();
        let mut cx = Context::from_waker(&waker);

        let mut consumer = Consumer::new(
            ShortString::from("test-consumer"),
            Arc::new(DefaultExecutor::default()),
        );

        assert_eq!(awoken_count.get(), 0);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Pending);

        consumer.cancel().unwrap();

        assert_eq!(awoken_count.get(), 1);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Ready(None));
    }

    #[test]
    fn stream_on_error() {
        let (waker, awoken_count) = new_count_waker();
        let mut cx = Context::from_waker(&waker);

        let mut consumer = Consumer::new(
            ShortString::from("test-consumer"),
            Arc::new(DefaultExecutor::default()),
        );

        assert_eq!(awoken_count.get(), 0);
        assert_eq!(consumer.poll_next_unpin(&mut cx), Poll::Pending);

        consumer.set_error(Error::InvalidFrameReceived).unwrap();

        assert_eq!(awoken_count.get(), 1);
        assert_eq!(
            consumer.poll_next_unpin(&mut cx),
            Poll::Ready(Some(Err(Error::InvalidFrameReceived)))
        );
    }
}
<|endoftext|>