<commit_before>use crate::{ExpectedSyscall, SyscallLogEntry};
use std::cell::Cell;

// TODO: Add Allow.

mod command_impl;

#[cfg(test)]
mod command_impl_tests;

// TODO: Add Exit.
// TODO: Add Memop.
// TODO: Add Subscribe.
mod raw_syscalls_impl;
mod thread_local;
mod yield_impl;

#[cfg(test)]
mod yield_impl_tests;

/// A fake implementation of the Tock kernel. Provides
/// `libtock_platform::Syscalls` by implementing
/// `libtock_platform::RawSyscalls`. Allows `fake::Driver`s to be attached, and
/// routes system calls to the correct fake driver.
///
/// Note that there can only be one `Kernel` instance per thread, as a
/// thread-local variable is used to implement `libtock_platform::RawSyscalls`.
/// As such, test code is given a `Rc<Kernel>` rather than a `Kernel` instance
/// directly. Because `Rc` is a shared reference, Kernel extensively uses
/// internal mutability.
// TODO: Define the `fake::Driver` trait and add support for fake drivers in
// Kernel.
pub struct Kernel {
    expected_syscalls: Cell<std::collections::VecDeque<ExpectedSyscall>>,

    // The location of the call to `new`. Used by report_leaked() to tell the
    // user which kernel they leaked in a unit test.
    new_location: &'static std::panic::Location<'static>,

    syscall_log: Cell<Vec<SyscallLogEntry>>,
}

impl Kernel {
    /// Creates a `Kernel` for this thread and returns a reference to it. This
    /// instance should be dropped at the end of the test, before this thread
    /// creates another `Kernel`.
    #[track_caller]
    pub fn new() -> std::rc::Rc<Kernel> {
        let rc = std::rc::Rc::new(Kernel {
            expected_syscalls: Default::default(),
            new_location: std::panic::Location::caller(),
            syscall_log: Default::default(),
        });
        thread_local::set_kernel(&rc);
        rc
    }

    /// Adds an ExpectedSyscall to the expected syscall queue.
    ///
    /// # What is the expected syscall queue?
    ///
    /// In addition to routing system calls to drivers, `Kernel` supports
    /// injecting artificial system call responses. The primary use case for
    /// this feature is to simulate errors without having to implement error
    /// simulation in each `fake::Driver`.
    ///
    /// The expected syscall queue is a FIFO queue containing anticipated
    /// upcoming system calls. It starts empty, and as long as it is empty, the
    /// expected syscall functionality does nothing. When the queue is nonempty
    /// and a system call is made, the system call is compared with the next
    /// queue entry. If the system call matches, then the action defined by the
    /// expected syscall is taken. If the call does not match, the call panics
    /// (to make the unit test fail).
    pub fn add_expected_syscall(&self, expected_syscall: ExpectedSyscall) {
        let mut queue = self.expected_syscalls.take();
        queue.push_back(expected_syscall);
        self.expected_syscalls.set(queue);
    }

    /// Returns the system call log and empties it.
    pub fn take_syscall_log(&self) -> Vec<SyscallLogEntry> {
        self.syscall_log.take()
    }
}

impl Drop for Kernel {
    fn drop(&mut self) {
        thread_local::clear_kernel();
    }
}

// -----------------------------------------------------------------------------
// Crate implementation details below.
// -----------------------------------------------------------------------------

impl Kernel {
    // Appends a log entry to the system call queue.
    fn log_syscall(&self, syscall: SyscallLogEntry) {
        let mut log = self.syscall_log.take();
        log.push(syscall);
        self.syscall_log.set(log);
    }

    // Retrieves the first syscall in the expected syscalls queue, removing it
    // from the queue. Returns None if the queue was empty.
    fn pop_expected_syscall(&self) -> Option<ExpectedSyscall> {
        let mut queue = self.expected_syscalls.take();
        let expected_syscall = queue.pop_front();
        self.expected_syscalls.set(queue);
        expected_syscall
    }

    // Panics, indicating that this Kernel was leaked. It is unlikely that this
    // panic will cause the correct test case to fail, but if this Kernel is
    // well-named the panic message should indicate where the leak occurred.
    fn report_leaked(&self) -> ! {
        panic!(
            "The fake::Kernel initialized at {}:{}:{} was not cleaned up; \
             perhaps a Rc<Kernel> was leaked?",
            self.new_location.file(),
            self.new_location.line(),
            self.new_location.column()
        );
    }
}

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

    #[test]
    fn expected_syscall_queue() {
        use libtock_platform::YieldNoWaitReturn::Upcall;
        use ExpectedSyscall::{YieldNoWait, YieldWait};
        let kernel = Kernel::new();
        assert_eq!(kernel.pop_expected_syscall(), None);
        kernel.add_expected_syscall(YieldNoWait {
            override_return: None,
        });
        kernel.add_expected_syscall(YieldNoWait {
            override_return: Some(Upcall),
        });
        assert_eq!(
            kernel.pop_expected_syscall(),
            Some(YieldNoWait {
                override_return: None
            })
        );
        kernel.add_expected_syscall(YieldWait { skip_upcall: false });
        assert_eq!(
            kernel.pop_expected_syscall(),
            Some(YieldNoWait {
                override_return: Some(Upcall)
            })
        );
        assert_eq!(
            kernel.pop_expected_syscall(),
            Some(YieldWait { skip_upcall: false })
        );
        assert_eq!(kernel.pop_expected_syscall(), None);
    }

    #[test]
    fn syscall_log() {
        use SyscallLogEntry::{YieldNoWait, YieldWait};
        let kernel = Kernel::new();
        assert_eq!(kernel.take_syscall_log(), []);
        kernel.log_syscall(YieldNoWait);
        kernel.log_syscall(YieldWait);
        assert_eq!(kernel.take_syscall_log(), [YieldNoWait, YieldWait]);
        kernel.log_syscall(YieldNoWait);
        assert_eq!(kernel.take_syscall_log(), [YieldNoWait]);
        assert_eq!(kernel.take_syscall_log(), []);
    }

    // Verifies the location propagates correctly into the report_leaked() error
    // message.
    #[test]
    fn name_to_report_leaked() {
        use std::panic::{catch_unwind, AssertUnwindSafe, Location};
        #[rustfmt::skip]
        let (kernel, new_location) = (Kernel::new(), Location::caller());
        let result = catch_unwind(AssertUnwindSafe(|| kernel.report_leaked()));
        let panic_arg = result.expect_err("Kernel::report_leaked did not panic");
        let message = panic_arg
            .downcast_ref::<String>()
            .expect("Wrong panic payload type");
        assert!(message.contains(&format!("{}:{}", new_location.file(), new_location.line())));
    }
}
<commit_msg>Don't compare `ExpectedSyscall` using `PartialEq`, use `matches!` instead.<commit_after>use crate::{ExpectedSyscall, SyscallLogEntry};
use std::cell::Cell;

// TODO: Add Allow.

mod command_impl;

#[cfg(test)]
mod command_impl_tests;

// TODO: Add Exit.
// TODO: Add Memop.
// TODO: Add Subscribe.
mod raw_syscalls_impl;
mod thread_local;
mod yield_impl;

#[cfg(test)]
mod yield_impl_tests;

/// A fake implementation of the Tock kernel. Provides
/// `libtock_platform::Syscalls` by implementing
/// `libtock_platform::RawSyscalls`. Allows `fake::Driver`s to be attached, and
/// routes system calls to the correct fake driver.
///
/// Note that there can only be one `Kernel` instance per thread, as a
/// thread-local variable is used to implement `libtock_platform::RawSyscalls`.
/// As such, test code is given a `Rc<Kernel>` rather than a `Kernel` instance
/// directly. Because `Rc` is a shared reference, Kernel extensively uses
/// internal mutability.
// TODO: Define the `fake::Driver` trait and add support for fake drivers in
// Kernel.
pub struct Kernel {
    expected_syscalls: Cell<std::collections::VecDeque<ExpectedSyscall>>,

    // The location of the call to `new`. Used by report_leaked() to tell the
    // user which kernel they leaked in a unit test.
    new_location: &'static std::panic::Location<'static>,

    syscall_log: Cell<Vec<SyscallLogEntry>>,
}

impl Kernel {
    /// Creates a `Kernel` for this thread and returns a reference to it. This
    /// instance should be dropped at the end of the test, before this thread
    /// creates another `Kernel`.
    #[track_caller]
    pub fn new() -> std::rc::Rc<Kernel> {
        let rc = std::rc::Rc::new(Kernel {
            expected_syscalls: Default::default(),
            new_location: std::panic::Location::caller(),
            syscall_log: Default::default(),
        });
        thread_local::set_kernel(&rc);
        rc
    }

    /// Adds an ExpectedSyscall to the expected syscall queue.
    ///
    /// # What is the expected syscall queue?
    ///
    /// In addition to routing system calls to drivers, `Kernel` supports
    /// injecting artificial system call responses. The primary use case for
    /// this feature is to simulate errors without having to implement error
    /// simulation in each `fake::Driver`.
    ///
    /// The expected syscall queue is a FIFO queue containing anticipated
    /// upcoming system calls. It starts empty, and as long as it is empty, the
    /// expected syscall functionality does nothing. When the queue is nonempty
    /// and a system call is made, the system call is compared with the next
    /// queue entry. If the system call matches, then the action defined by the
    /// expected syscall is taken. If the call does not match, the call panics
    /// (to make the unit test fail).
    pub fn add_expected_syscall(&self, expected_syscall: ExpectedSyscall) {
        let mut queue = self.expected_syscalls.take();
        queue.push_back(expected_syscall);
        self.expected_syscalls.set(queue);
    }

    /// Returns the system call log and empties it.
    pub fn take_syscall_log(&self) -> Vec<SyscallLogEntry> {
        self.syscall_log.take()
    }
}

impl Drop for Kernel {
    fn drop(&mut self) {
        thread_local::clear_kernel();
    }
}

// -----------------------------------------------------------------------------
// Crate implementation details below.
// -----------------------------------------------------------------------------

impl Kernel {
    // Appends a log entry to the system call queue.
    fn log_syscall(&self, syscall: SyscallLogEntry) {
        let mut log = self.syscall_log.take();
        log.push(syscall);
        self.syscall_log.set(log);
    }

    // Retrieves the first syscall in the expected syscalls queue, removing it
    // from the queue. Returns None if the queue was empty.
    fn pop_expected_syscall(&self) -> Option<ExpectedSyscall> {
        let mut queue = self.expected_syscalls.take();
        let expected_syscall = queue.pop_front();
        self.expected_syscalls.set(queue);
        expected_syscall
    }

    // Panics, indicating that this Kernel was leaked. It is unlikely that this
    // panic will cause the correct test case to fail, but if this Kernel is
    // well-named the panic message should indicate where the leak occurred.
    fn report_leaked(&self) -> ! {
        panic!(
            "The fake::Kernel initialized at {}:{}:{} was not cleaned up; \
             perhaps a Rc<Kernel> was leaked?",
            self.new_location.file(),
            self.new_location.line(),
            self.new_location.column()
        );
    }
}

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

    #[test]
    fn expected_syscall_queue() {
        use libtock_platform::YieldNoWaitReturn::Upcall;
        use std::matches;
        use ExpectedSyscall::{YieldNoWait, YieldWait};
        let kernel = Kernel::new();
        assert!(matches!(kernel.pop_expected_syscall(), None));
        kernel.add_expected_syscall(YieldNoWait {
            override_return: None,
        });
        kernel.add_expected_syscall(YieldNoWait {
            override_return: Some(Upcall),
        });
        assert!(matches!(
            kernel.pop_expected_syscall(),
            Some(YieldNoWait {
                override_return: None
            })
        ));
        kernel.add_expected_syscall(YieldWait { skip_upcall: false });
        assert!(matches!(
            kernel.pop_expected_syscall(),
            Some(YieldNoWait {
                override_return: Some(Upcall)
            })
        ));
        assert!(matches!(
            kernel.pop_expected_syscall(),
            Some(YieldWait { skip_upcall: false })
        ));
        assert!(matches!(kernel.pop_expected_syscall(), None));
    }

    #[test]
    fn syscall_log() {
        use SyscallLogEntry::{YieldNoWait, YieldWait};
        let kernel = Kernel::new();
        assert_eq!(kernel.take_syscall_log(), []);
        kernel.log_syscall(YieldNoWait);
        kernel.log_syscall(YieldWait);
        assert_eq!(kernel.take_syscall_log(), [YieldNoWait, YieldWait]);
        kernel.log_syscall(YieldNoWait);
        assert_eq!(kernel.take_syscall_log(), [YieldNoWait]);
        assert_eq!(kernel.take_syscall_log(), []);
    }

    // Verifies the location propagates correctly into the report_leaked() error
    // message.
    #[test]
    fn name_to_report_leaked() {
        use std::panic::{catch_unwind, AssertUnwindSafe, Location};
        #[rustfmt::skip]
        let (kernel, new_location) = (Kernel::new(), Location::caller());
        let result = catch_unwind(AssertUnwindSafe(|| kernel.report_leaked()));
        let panic_arg = result.expect_err("Kernel::report_leaked did not panic");
        let message = panic_arg
            .downcast_ref::<String>()
            .expect("Wrong panic payload type");
        assert!(message.contains(&format!("{}:{}", new_location.file(), new_location.line())));
    }
}
<|endoftext|>


<commit_before><commit_msg>Removed unused imports.<commit_after><|endoftext|>


<commit_before><commit_msg>[Encode] Minor documentation pack.<commit_after><|endoftext|>


<commit_before>use level::Level;

use na::Vec2;

use rand::os::OsRng;
use rand::XorShiftRng;
use rand::Rand;
use rand::Rng;

pub struct MazeGen {
    pos: Vec2<usize>,
    rand: XorShiftRng,
}

impl MazeGen {
    pub fn new(x: usize, y: usize) -> MazeGen {
        MazeGen{
            pos: Vec2::new(x,y),
            rand: XorShiftRng::rand(&mut OsRng::new().unwrap()),
        }
    }

    pub fn generate(level: &mut Level) {

    }
}
<commit_msg>mazegeneration continued<commit_after>use level::Level;

use na::Vec2;

use rand::os::OsRng;
use rand::XorShiftRng;
use rand::Rand;
use rand::Rng;

pub struct MazeGen {
    pos: Vec2<usize>,
    rand: XorShiftRng,
}

impl MazeGen {
    pub fn new(x: usize, y: usize) -> MazeGen {
        MazeGen{
            pos: Vec2::new(x,y),
            rand: XorShiftRng::rand(&mut OsRng::new().unwrap()),
        }
    }

    pub fn get_start_pos(&self) -> &Vec2 {
        self.pos
    }

    pub fn set_start_pos(&mut self, pos: &Vec2) {
        self.pos = pos.clone();
    }

    pub fn generate(&self, level: &mut Level) {

    }

}
<|endoftext|>


<commit_before>use std::any::Any;
use std::cmp::{max, min};
use std::mem;
use std::rc::Rc;

use term_input::{Key, Arrow};
use termbox_simple::Termbox;

use trie::Trie;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet, Widget};
use utils;

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    mode : Mode,
}

enum Mode {
    /// Editing the buffer
    Edit,

    /// Browsing history
    History(i32),

    /// Auto-completing a nick in channel
    Autocomplete {
        original_buffer    : Vec<char>,
        insertion_point    : usize,
        word_starts        : usize,
        completions        : Vec<String>,
        current_completion : usize,
    }
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            mode: Mode::Edit,
        }
    }

    pub fn resize_(&mut self, width : i32, _ : i32) {
        self.width = width;
        self.move_cursor_to_end();
    }

    pub fn draw_(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        match self.mode {
            Mode::Edit => {
                draw_line(tb, &self.buffer, pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::History(hist_curs) => {
                draw_line(tb,
                          &self.history[hist_curs as usize],
                          pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::Autocomplete {
                ref original_buffer, insertion_point, word_starts,
                ref completions, current_completion
            } => {
                // draw a placeholder for the cursor
                tb.change_cell(pos_x + self.cursor - self.scroll, pos_y,
                               ' ',
                               style::USER_MSG.fg, style::USER_MSG.bg);

                let completion : &str = &completions[current_completion];

                let mut orig_buf_iter = original_buffer.iter().cloned();
                let mut completion_iter = completion.chars();

                let iter : utils::InsertIterator<char> =
                    utils::insert_iter(&mut orig_buf_iter, &mut completion_iter, insertion_point);

                for (char_idx, char) in iter.enumerate() {
                    if char_idx >= ((self.scroll + self.width) as usize) {
                        break;
                    }

                    if char_idx >= self.scroll as usize {
                        if char_idx >= word_starts &&
                                char_idx < insertion_point + completion.len() {
                            tb.change_cell(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                           char,
                                           style::COMPLETION.fg, style::COMPLETION.bg);
                        } else {
                            tb.change_cell(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                           char,
                                           style::USER_MSG.fg, style::USER_MSG.bg);
                        }

                    }
                }

                tb.set_cursor(pos_x + self.cursor - self.scroll, pos_y);
            },
        }
    }

    pub fn keypressed_(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                }

                else if ch == 'e' {
                    self.move_cursor_to_end();
                    WidgetRet::KeyHandled
                }

                else if ch == 'k' {
                    if self.cursor != self.line_len() {
                        self.modify();
                        self.buffer.drain(self.cursor as usize ..);
                    }
                    WidgetRet::KeyHandled
                }

                else if ch == 'w' {
                    self.consume_word_before_curs();
                    WidgetRet::KeyHandled
                }

                else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Arrow(Arrow::Left) => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Arrow(Arrow::Right) => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                if self.line_len() > 0 {
                    self.modify();

                    let ret = mem::replace(&mut self.buffer, Vec::new());
                    if self.history.len() == HIST_SIZE {
                        let mut reuse = self.history.remove(0);
                        reuse.clear();
                        reuse.extend_from_slice(&ret);
                        self.history.push(reuse);
                    } else {
                        self.history.push(ret.clone());
                    }

                    self.move_cursor(0);

                    WidgetRet::Input(ret)
                } else {
                    WidgetRet::KeyHandled
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history or autocompletion list

            Key::Arrow(Arrow::Up) => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {
                        if !self.history.is_empty() {
                            self.mode = Mode::History((self.history.len() as i32) - 1);
                            self.move_cursor_to_end();
                        }
                    },
                    Mode::History(hist_curs) => {
                        self.mode = Mode::History(
                            if hist_curs > 0 { hist_curs - 1 } else { hist_curs });
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion != completions.len() - 1 {
                                current_completion + 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            Key::Arrow(Arrow::Down) => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {},
                    Mode::History(hist_curs) => {
                        if hist_curs != (self.history.len() - 1) as i32 {
                            self.mode = Mode::History(hist_curs + 1);
                        } else {
                            self.mode = Mode::Edit;
                        }
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion > 0 {
                                current_completion - 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    fn consume_word_before_curs(&mut self) {
        // No modifications can happen if the scroll is at the beginning
        if self.cursor == 0 {
            return;
        }

        self.modify();

        let char = self.buffer[(self.cursor - 1) as usize];

        // Try to imitate vim's behaviour here.
        if char.is_whitespace() {
            self.consume_before(|c| c.is_whitespace());
            self.consume_before(|c| c.is_alphanumeric());
        } else {
            let char = self.buffer[(self.cursor - 1) as usize];
            if char.is_alphanumeric() {
                self.consume_before(|c| c.is_alphanumeric());
            } else if self.cursor != 0 { // consume at least one char
                let cursor = self.cursor;
                self.buffer.remove(cursor as usize - 1);
                self.move_cursor(cursor - 1);
            }
        }
    }

    fn consume_before<F>(&mut self, f : F) where F : Fn(char) -> bool {
        let end_range = self.cursor as usize;
        let mut begin_range = self.cursor - 1;
        while begin_range >= 0 && f(self.buffer[begin_range as usize]) {
            begin_range -= 1;
        }
        self.buffer.drain(((begin_range + 1) as usize) .. end_range);
        self.move_cursor(begin_range + 1);
    }

    // Ignoring auto-completions
    fn shown_line(&self) -> &Vec<char> {
        match self.mode {
            Mode::Edit | Mode::Autocomplete { .. } => &self.buffer,
            Mode::History(hist_curs) => &self.history[hist_curs as usize],
        }
    }

    fn line_len(&self) -> i32 {
        match self.mode {
            Mode::Edit => {
                self.buffer.len() as i32
            },
            Mode::History(hist_curs) => {
                self.history[hist_curs as usize].len() as i32
            },
            Mode::Autocomplete { ref original_buffer, ref completions, current_completion, .. } => {
                (original_buffer.len() + completions[current_completion].len()) as i32
            },
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    fn in_autocomplete(&self) -> bool {
        match self.mode {
            Mode::Autocomplete { .. } => true,
            _ => false
        }
    }

    fn modify(&mut self) {
        match self.mode {
            Mode::Edit => {},
            Mode::History(hist_idx) => {
                self.buffer.clear();
                self.buffer.extend_from_slice(&self.history[hist_idx as usize]);
            },
            Mode::Autocomplete {
                ref mut original_buffer,
                mut insertion_point,
                ref mut completions,
                current_completion,
                ..
            } => {
                let mut buffer  : Vec<char>   = mem::replace(original_buffer, vec![]);
                let completions : Vec<String> = mem::replace(completions, vec![]);
                let word = &completions[current_completion];

                // FIXME: This is inefficient
                for char in word.chars() {
                    buffer.insert(insertion_point, char);
                    insertion_point += 1;
                }

                self.buffer = buffer;
            }
        }

        self.mode = Mode::Edit;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    fn move_cursor_to_end(&mut self) {
        let cursor = self.line_len();
        self.move_cursor(cursor);
    }

    fn move_cursor(&mut self, cursor : i32) {
        assert!(cursor >= 0 && cursor <= self.line_len());
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}

fn draw_line(tb: &mut Termbox, line : &[char], pos_x : i32, pos_y : i32, scroll : i32, width : i32, cursor : i32) {
    let slice : &[char] = &line[ scroll as usize .. min(line.len(), (scroll + width) as usize) ];

    termbox::print_chars(tb, pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

    // On my terminal the cursor is only shown when there's a character
    // under it.
    if cursor as usize >= line.len() {
        tb.change_cell(pos_x + cursor - scroll, pos_y,
                       ' ',
                       style::USER_MSG.fg, style::USER_MSG.bg);
    }
    tb.set_cursor(pos_x + cursor - scroll, pos_y);
}

impl Widget for TextField {
    fn resize(&mut self, width : i32, height : i32) {
        self.resize_(width, height);
    }

    fn draw(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        self.draw_(tb, pos_x, pos_y);
    }

    fn keypressed(&mut self, key : Key) -> WidgetRet {
        self.keypressed_(key)
    }

    // fn autocomplete(&mut self, dict : &Trie) {
    fn event(&mut self, ev: Box<Any>) -> WidgetRet {
        match ev.downcast_ref::<Rc<Trie>>() {
            None => WidgetRet::KeyIgnored,
            Some(dict) => {
                if self.in_autocomplete() {
                    // AWFUL CODE YO
                    self.keypressed(Key::Arrow(Arrow::Up));
                    return WidgetRet::KeyHandled;
                }

                let cursor_right = self.cursor;
                let mut cursor_left = max(0, cursor_right - 1);

                let completions = {
                    let line = self.shown_line();

                    while cursor_left >= 0
                        && line.get(cursor_left as usize).map(|c| c.is_alphanumeric()).unwrap_or(false) {
                            cursor_left -= 1;
                        }

                    let word = {
                        if cursor_left == cursor_right {
                            &[]
                        } else {
                            cursor_left += 1;
                            &line[ (cursor_left as usize) .. (cursor_right as usize) ]
                        }
                    };

                    dict.drop_pfx(&mut word.iter().cloned())
                };

                if !completions.is_empty() {
                    let completion_len = completions[0].len();
                    self.mode = Mode::Autocomplete {
                        original_buffer: self.shown_line().to_owned(),
                        insertion_point: self.cursor as usize,
                        word_starts: cursor_left as usize,
                        completions: completions,
                        current_completion: 0,
                    };
                    let cursor = self.cursor;
                    self.move_cursor(cursor + completion_len as i32);
                }

                WidgetRet::KeyHandled
            }
        }
    }
}
<commit_msg>Minor refactoring: remove some redundant lambdas<commit_after>use std::any::Any;
use std::cmp::{max, min};
use std::mem;
use std::rc::Rc;

use term_input::{Key, Arrow};
use termbox_simple::Termbox;

use trie::Trie;
use tui::style;
use tui::termbox;
use tui::widget::{WidgetRet, Widget};
use utils;

// TODO: Make these settings
const SCROLLOFF : i32 = 5;
const HIST_SIZE : usize = 30;

pub struct TextField {
    /// The message that's currently being edited (not yet sent)
    buffer : Vec<char>,

    /// Cursor in currently shown line
    cursor : i32,

    /// Horizontal scroll
    scroll : i32,

    /// Width of the widget
    width : i32,

    /// A history of sent messages/commands. Once added messages are never
    /// modified. A modification attempt should result in a new buffer with a
    /// copy of the vector in history. (old contents of the buffer will be lost)
    history : Vec<Vec<char>>,

    mode : Mode,
}

enum Mode {
    /// Editing the buffer
    Edit,

    /// Browsing history
    History(i32),

    /// Auto-completing a nick in channel
    Autocomplete {
        original_buffer    : Vec<char>,
        insertion_point    : usize,
        word_starts        : usize,
        completions        : Vec<String>,
        current_completion : usize,
    }
}

impl TextField {
    pub fn new(width : i32) -> TextField {
        TextField {
            buffer: Vec::with_capacity(512),
            cursor: 0,
            scroll: 0,
            width: width,
            history: Vec::with_capacity(HIST_SIZE),
            mode: Mode::Edit,
        }
    }

    pub fn resize_(&mut self, width : i32, _ : i32) {
        self.width = width;
        self.move_cursor_to_end();
    }

    pub fn draw_(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        match self.mode {
            Mode::Edit => {
                draw_line(tb, &self.buffer, pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::History(hist_curs) => {
                draw_line(tb,
                          &self.history[hist_curs as usize],
                          pos_x, pos_y, self.scroll, self.width, self.cursor);
            },
            Mode::Autocomplete {
                ref original_buffer, insertion_point, word_starts,
                ref completions, current_completion
            } => {
                // draw a placeholder for the cursor
                tb.change_cell(pos_x + self.cursor - self.scroll, pos_y,
                               ' ',
                               style::USER_MSG.fg, style::USER_MSG.bg);

                let completion : &str = &completions[current_completion];

                let mut orig_buf_iter = original_buffer.iter().cloned();
                let mut completion_iter = completion.chars();

                let iter : utils::InsertIterator<char> =
                    utils::insert_iter(&mut orig_buf_iter, &mut completion_iter, insertion_point);

                for (char_idx, char) in iter.enumerate() {
                    if char_idx >= ((self.scroll + self.width) as usize) {
                        break;
                    }

                    if char_idx >= self.scroll as usize {
                        if char_idx >= word_starts &&
                                char_idx < insertion_point + completion.len() {
                            tb.change_cell(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                           char,
                                           style::COMPLETION.fg, style::COMPLETION.bg);
                        } else {
                            tb.change_cell(pos_x + (char_idx as i32) - self.scroll, pos_y,
                                           char,
                                           style::USER_MSG.fg, style::USER_MSG.bg);
                        }

                    }
                }

                tb.set_cursor(pos_x + self.cursor - self.scroll, pos_y);
            },
        }
    }

    pub fn keypressed_(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Char(ch) => {
                self.modify();
                self.buffer.insert(self.cursor as usize, ch);
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Backspace => {
                if self.cursor > 0 {
                    self.modify();
                    self.buffer.remove(self.cursor as usize - 1);
                    self.dec_cursor();
                }
                WidgetRet::KeyHandled
            },

            Key::Ctrl(ch) => {
                if ch == 'a' {
                    self.move_cursor(0);
                    WidgetRet::KeyHandled
                }

                else if ch == 'e' {
                    self.move_cursor_to_end();
                    WidgetRet::KeyHandled
                }

                else if ch == 'k' {
                    if self.cursor != self.line_len() {
                        self.modify();
                        self.buffer.drain(self.cursor as usize ..);
                    }
                    WidgetRet::KeyHandled
                }

                else if ch == 'w' {
                    self.consume_word_before_curs();
                    WidgetRet::KeyHandled
                }

                else {
                    WidgetRet::KeyIgnored
                }
            },

            Key::Arrow(Arrow::Left) => {
                self.dec_cursor();
                WidgetRet::KeyHandled
            },

            Key::Arrow(Arrow::Right) => {
                self.inc_cursor();
                WidgetRet::KeyHandled
            },

            Key::Enter => {
                if self.line_len() > 0 {
                    self.modify();

                    let ret = mem::replace(&mut self.buffer, Vec::new());
                    if self.history.len() == HIST_SIZE {
                        let mut reuse = self.history.remove(0);
                        reuse.clear();
                        reuse.extend_from_slice(&ret);
                        self.history.push(reuse);
                    } else {
                        self.history.push(ret.clone());
                    }

                    self.move_cursor(0);

                    WidgetRet::Input(ret)
                } else {
                    WidgetRet::KeyHandled
                }
            },

            ////////////////////////////////////////////////////////////////////
            // Scrolling in history or autocompletion list

            Key::Arrow(Arrow::Up) => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {
                        if !self.history.is_empty() {
                            self.mode = Mode::History((self.history.len() as i32) - 1);
                            self.move_cursor_to_end();
                        }
                    },
                    Mode::History(hist_curs) => {
                        self.mode = Mode::History(
                            if hist_curs > 0 { hist_curs - 1 } else { hist_curs });
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion != completions.len() - 1 {
                                current_completion + 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            Key::Arrow(Arrow::Down) => {
                let mode = mem::replace(&mut self.mode, Mode::Edit);

                match mode {
                    Mode::Edit => {},
                    Mode::History(hist_curs) => {
                        if hist_curs != (self.history.len() - 1) as i32 {
                            self.mode = Mode::History(hist_curs + 1);
                        } else {
                            self.mode = Mode::Edit;
                        }
                        self.move_cursor_to_end();
                    },
                    Mode::Autocomplete {
                        original_buffer, insertion_point,
                        word_starts, completions, current_completion, ..
                    } => {
                        let current_completion =
                            if current_completion > 0 {
                                current_completion - 1
                            } else {
                                current_completion
                            };

                        let cursor = (insertion_point + completions[current_completion].len()) as i32;

                        self.mode = Mode::Autocomplete {
                            original_buffer: original_buffer,
                            insertion_point: insertion_point,
                            word_starts: word_starts,
                            completions: completions,
                            current_completion: current_completion,
                        };

                        self.move_cursor(cursor);
                    },
                }

                WidgetRet::KeyHandled
            },

            ////////////////////////////////////////////////////////////////////

            _ => WidgetRet::KeyIgnored,
        }
    }

    fn consume_word_before_curs(&mut self) {
        // No modifications can happen if the scroll is at the beginning
        if self.cursor == 0 {
            return;
        }

        self.modify();

        let char = self.buffer[(self.cursor - 1) as usize];

        // Try to imitate vim's behaviour here.
        if char.is_whitespace() {
            self.consume_before(char::is_whitespace);
            self.consume_before(char::is_alphanumeric);
        } else {
            let char = self.buffer[(self.cursor - 1) as usize];
            if char.is_alphanumeric() {
                self.consume_before(char::is_alphanumeric);
            } else if self.cursor != 0 { // consume at least one char
                let cursor = self.cursor;
                self.buffer.remove(cursor as usize - 1);
                self.move_cursor(cursor - 1);
            }
        }
    }

    fn consume_before<F>(&mut self, f : F) where F : Fn(char) -> bool {
        let end_range = self.cursor as usize;
        let mut begin_range = self.cursor - 1;
        while begin_range >= 0 && f(self.buffer[begin_range as usize]) {
            begin_range -= 1;
        }
        self.buffer.drain(((begin_range + 1) as usize) .. end_range);
        self.move_cursor(begin_range + 1);
    }

    // Ignoring auto-completions
    fn shown_line(&self) -> &Vec<char> {
        match self.mode {
            Mode::Edit | Mode::Autocomplete { .. } => &self.buffer,
            Mode::History(hist_curs) => &self.history[hist_curs as usize],
        }
    }

    fn line_len(&self) -> i32 {
        match self.mode {
            Mode::Edit => {
                self.buffer.len() as i32
            },
            Mode::History(hist_curs) => {
                self.history[hist_curs as usize].len() as i32
            },
            Mode::Autocomplete { ref original_buffer, ref completions, current_completion, .. } => {
                (original_buffer.len() + completions[current_completion].len()) as i32
            },
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    fn in_autocomplete(&self) -> bool {
        match self.mode {
            Mode::Autocomplete { .. } => true,
            _ => false
        }
    }

    fn modify(&mut self) {
        match self.mode {
            Mode::Edit => {},
            Mode::History(hist_idx) => {
                self.buffer.clear();
                self.buffer.extend_from_slice(&self.history[hist_idx as usize]);
            },
            Mode::Autocomplete {
                ref mut original_buffer,
                mut insertion_point,
                ref mut completions,
                current_completion,
                ..
            } => {
                let mut buffer  : Vec<char>   = mem::replace(original_buffer, vec![]);
                let completions : Vec<String> = mem::replace(completions, vec![]);
                let word = &completions[current_completion];

                // FIXME: This is inefficient
                for char in word.chars() {
                    buffer.insert(insertion_point, char);
                    insertion_point += 1;
                }

                self.buffer = buffer;
            }
        }

        self.mode = Mode::Edit;
    }

    ////////////////////////////////////////////////////////////////////////////
    // Manipulating cursor

    fn inc_cursor(&mut self) {
        let cur = min(self.line_len(), self.cursor + 1);
        self.move_cursor(cur);
    }

    fn dec_cursor(&mut self) {
        let cur = max(0, self.cursor - 1);
        self.move_cursor(cur);
    }

    fn move_cursor_to_end(&mut self) {
        let cursor = self.line_len();
        self.move_cursor(cursor);
    }

    fn move_cursor(&mut self, cursor : i32) {
        assert!(cursor >= 0 && cursor <= self.line_len());
        self.cursor = cursor;

        if self.line_len() + 1 < self.width {
            self.scroll = 0;
        } else {
            let scrolloff = { if self.width < 2 * SCROLLOFF + 1 { 0 } else { SCROLLOFF } };

            let left_end  = self.scroll;
            let right_end = self.scroll + self.width;

            if cursor - scrolloff < left_end {
                self.scroll = max(0, cursor - scrolloff);
            } else if cursor + scrolloff >= right_end {
                self.scroll = min(// +1 because cursor should be visible, i.e.
                                  // right_end > cursor should hold after this
                                  max(0, cursor + 1 + scrolloff - self.width),
                                  // +1 because cursor goes one more character
                                  // after the buffer, to be able to add chars
                                  max(0, self.line_len() + 1 - self.width));
            }
        }
    }
}

fn draw_line(tb: &mut Termbox, line : &[char], pos_x : i32, pos_y : i32, scroll : i32, width : i32, cursor : i32) {
    let slice : &[char] = &line[ scroll as usize .. min(line.len(), (scroll + width) as usize) ];

    termbox::print_chars(tb, pos_x, pos_y, style::USER_MSG.fg, style::USER_MSG.bg, slice);

    // On my terminal the cursor is only shown when there's a character
    // under it.
    if cursor as usize >= line.len() {
        tb.change_cell(pos_x + cursor - scroll, pos_y,
                       ' ',
                       style::USER_MSG.fg, style::USER_MSG.bg);
    }
    tb.set_cursor(pos_x + cursor - scroll, pos_y);
}

impl Widget for TextField {
    fn resize(&mut self, width : i32, height : i32) {
        self.resize_(width, height);
    }

    fn draw(&self, tb : &mut Termbox, pos_x : i32, pos_y : i32) {
        self.draw_(tb, pos_x, pos_y);
    }

    fn keypressed(&mut self, key : Key) -> WidgetRet {
        self.keypressed_(key)
    }

    // fn autocomplete(&mut self, dict : &Trie) {
    fn event(&mut self, ev: Box<Any>) -> WidgetRet {
        match ev.downcast_ref::<Rc<Trie>>() {
            None => WidgetRet::KeyIgnored,
            Some(dict) => {
                if self.in_autocomplete() {
                    // AWFUL CODE YO
                    self.keypressed(Key::Arrow(Arrow::Up));
                    return WidgetRet::KeyHandled;
                }

                let cursor_right = self.cursor;
                let mut cursor_left = max(0, cursor_right - 1);

                let completions = {
                    let line = self.shown_line();

                    while cursor_left >= 0
                        && line.get(cursor_left as usize).map(|c| c.is_alphanumeric()).unwrap_or(false) {
                            cursor_left -= 1;
                        }

                    let word = {
                        if cursor_left == cursor_right {
                            &[]
                        } else {
                            cursor_left += 1;
                            &line[ (cursor_left as usize) .. (cursor_right as usize) ]
                        }
                    };

                    dict.drop_pfx(&mut word.iter().cloned())
                };

                if !completions.is_empty() {
                    let completion_len = completions[0].len();
                    self.mode = Mode::Autocomplete {
                        original_buffer: self.shown_line().to_owned(),
                        insertion_point: self.cursor as usize,
                        word_starts: cursor_left as usize,
                        completions: completions,
                        current_completion: 0,
                    };
                    let cursor = self.cursor;
                    self.move_cursor(cursor + completion_len as i32);
                }

                WidgetRet::KeyHandled
            }
        }
    }
}
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::method_chain_args;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::symbol::sym;

use super::STRING_EXTEND_CHARS;

pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
    let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
    if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
        let arg = &args[1];
        if let Some(arglists) = method_chain_args(arg, &["chars"]) {
            let target = &arglists[0][0];
            let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
            let ref_str = if *self_ty.kind() == ty::Str {
                ""
            } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
                "&"
            } else {
                return;
            };

            let mut applicability = Applicability::MachineApplicable;
            span_lint_and_sugg(
                cx,
                STRING_EXTEND_CHARS,
                expr.span,
                "calling `.extend(_.chars())`",
                "try this",
                format!(
                    "{}.push_str({}{})",
                    snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
                    ref_str,
                    snippet_with_applicability(cx, target.span, "..", &mut applicability)
                ),
                applicability,
            );
        }
    }
}
<commit_msg>refactor string_extend_chars: return when obj type is not string<commit_after>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::method_chain_args;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::symbol::sym;

use super::STRING_EXTEND_CHARS;

pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
    let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
    if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
        return;
    }
    let arg = &args[1];
    if let Some(arglists) = method_chain_args(arg, &["chars"]) {
        let target = &arglists[0][0];
        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
        let ref_str = if *self_ty.kind() == ty::Str {
            ""
        } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
            "&"
        } else {
            return;
        };

        let mut applicability = Applicability::MachineApplicable;
        span_lint_and_sugg(
            cx,
            STRING_EXTEND_CHARS,
            expr.span,
            "calling `.extend(_.chars())`",
            "try this",
            format!(
                "{}.push_str({}{})",
                snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
                ref_str,
                snippet_with_applicability(cx, target.span, "..", &mut applicability)
            ),
            applicability,
        );
    }
}
<|endoftext|>


<commit_before>#![feature(if_let)]
#![feature(phase)]
#![feature(unboxed_closures)]
#![feature(macro_rules)]

extern crate baps3_protocol;
#[phase(plugin)] extern crate baps3_cli;
extern crate baps3_cli;
extern crate docopt;
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;

use std::io::IoResult;

use baps3_cli::client::{Client, Request, Response};

docopt!(Args deriving Show, "
Loads a file into a BAPS3 server.

Usage:
  baps3-load [options] <file>

Options:
  -h, --help             Show this message.
  -t, --target <target>  The target BAPS3 server (host:port).
                         [Default: localhost:1350]
  -v, --verbose          Prints a trail of miscellaneous information
                         about the action.
")

/// Creates a vector of string-slices from a vector of strings.
///
/// The slice vector lives as long as the original vector.
pub fn slicify<'a>(vec: &'a Vec<String>) -> Vec<&'a str> {
    vec.iter().map(|x| x.as_slice()).collect::<Vec<&str>>()
}

pub fn slicify_msg<'a>(code: &'a String,
                       vec: &'a Vec<String>) -> Vec<&'a str> {
    let mut v = slicify(vec);
    v.insert(0, &**code);
    v
}

type Logger<'a> = |&str|:'a;
macro_rules! log(
    ($l:ident, $($arg:tt)*) => (
        let _ = (*$l)(&*format!($($arg)*));
    )
)

fn check_baps3(log: &mut Logger, Client{request_tx, response_rx}: Client)
  -> IoResult<Client> {
    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["OHAI", ident] => {
                        log!(log, "Server ident: {}", ident);
                        break 'l;
                    }
                    _ => return Err(std::io::IoError {
                        kind: std::io::IoErrorKind::OtherIoError,
                        desc: "not a BAPS3 server",
                        detail: None
                    })
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn check_features(log: &mut Logger,
                  Client{request_tx, response_rx}: Client)
  -> IoResult<Client> {
    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["FEATURES", fs..] => {
                        log!(log, "Server features: {}", fs);
                        break 'l;
                    }
                    _ => return Err(std::io::IoError {
                        kind: std::io::IoErrorKind::OtherIoError,
                        desc: "expected FEATURES here",
                        detail: None
                    })
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn send_command(log: &mut Logger,
                Client{request_tx, response_rx}: Client,
                word: &str, args: &[&str]) -> IoResult<Client> {
    log!(log, "Sending command: {} {}", word, args);

    let oword = word.into_string();
    let oargs = args.iter().map(|arg| arg.into_string()).collect();
    request_tx.send(Request::SendMessage(oword, oargs));

    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["OKAY", cword, cargs..]
                      if cword == word && cargs == args => {
                        log!(log, "success!");
                        break 'l;
                    },
                    ["WHAT", advice, cword, cargs..]
                      if cword == word && cargs == args => {
                        werr!("command invalid: {}", advice);
                        break 'l;
                    },
                    ["FAIL", advice, cword, cargs..]
                      if cword == word && cargs == args => {
                        werr!("command failed: {}", advice);
                        break 'l;
                    },
                    _ => ()
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn quit_client(log: &mut Logger, Client{request_tx, ..}: Client)
  -> IoResult<()> {
    log!(log, "Closing client connection");

    request_tx.send(Request::Quit);
    Ok(())
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

    let mut log: Logger = if args.flag_verbose {
        |s: &str| { let _ = std::io::stderr().write_line(s); }
    } else {
        |_| {}
    };

    Client
     ::new(&*args.flag_target)
      .and_then(|c| check_baps3(&mut log, c))
      .and_then(|c| check_features(&mut log, c))
      .and_then(|c| send_command(&mut log, c, "load", &[&*args.arg_file]))
      .and_then(|c| quit_client(&mut log, c))
      .unwrap_or_else(|e| werr!("error: {}", e));
}<commit_msg>Clean up load code a little.<commit_after>#![feature(if_let)]
#![feature(phase)]
#![feature(unboxed_closures)]
#![feature(macro_rules)]

extern crate baps3_protocol;
#[phase(plugin)] extern crate baps3_cli;
extern crate baps3_cli;
extern crate docopt;
extern crate serialize;
#[phase(plugin)] extern crate docopt_macros;

use std::io::IoResult;

use baps3_cli::client::{Client, Request, Response};

docopt!(Args deriving Show, "
Loads a file into a BAPS3 server.

Usage:
  baps3-load [options] <file>

Options:
  -h, --help             Show this message.
  -t, --target <target>  The target BAPS3 server (host:port).
                         [Default: localhost:1350]
  -v, --verbose          Prints a trail of miscellaneous information
                         about the action.
")

/// Creates a vector of string-slices from a vector of strings.
///
/// The slice vector lives as long as the original vector.
pub fn slicify<'a>(vec: &'a Vec<String>) -> Vec<&'a str> {
    vec.iter().map(|x| x.as_slice()).collect::<Vec<&str>>()
}

pub fn slicify_msg<'a>(code: &'a String,
                       vec: &'a Vec<String>) -> Vec<&'a str> {
    let mut v = slicify(vec);
    v.insert(0, &**code);
    v
}

type Logger<'a> = |&str|:'a;
macro_rules! log(
    ($l:ident, $($arg:tt)*) => (
        let _ = (*$l)(&*format!($($arg)*));
    )
)

fn check_baps3(log: &mut Logger, Client{request_tx, response_rx}: Client)
  -> IoResult<Client> {
    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["OHAI", ident] => {
                        log!(log, "Server ident: {}", ident);
                        break 'l;
                    }
                    _ => return Err(std::io::IoError {
                        kind: std::io::IoErrorKind::OtherIoError,
                        desc: "not a BAPS3 server",
                        detail: None
                    })
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn check_features(log: &mut Logger,
                  needed: &[&str],
                  Client{request_tx, response_rx}: Client)
  -> IoResult<Client> {
    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["FEATURES", fs..] => {
                        log!(log, "Server features: {}", fs);

                        for n in needed.iter() {
                            if !fs.contains(n) {
                                return Err(std::io::IoError {
                                    kind: std::io::IoErrorKind::OtherIoError,
                                    desc: "insufficient features",
                                    detail: Some(format!("have: {} want: {}",
                                                         fs,
                                                         needed))
                                })
                            }
                        }

                        break 'l;
                    }
                    _ => return Err(std::io::IoError {
                        kind: std::io::IoErrorKind::OtherIoError,
                        desc: "expected FEATURES here",
                        detail: None
                    })
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn send_command(log: &mut Logger,
                Client{request_tx, response_rx}: Client,
                word: &str, args: &[&str]) -> IoResult<Client> {
    log!(log, "Sending command: {} {}", word, args);

    let oword = word.into_string();
    let oargs = args.iter().map(|arg| arg.into_string()).collect();
    request_tx.send(Request::SendMessage(oword, oargs));

    'l: loop {
        match response_rx.recv_opt() {
            Ok(Response::Message(code, msg)) => {
                match &*slicify_msg(&code, &msg) {
                    ["OKAY", cword, cargs..]
                      if cword == word && cargs == args => {
                        log!(log, "success!");
                        break 'l;
                    },
                    ["WHAT", advice, cword, cargs..]
                      if cword == word && cargs == args => {
                        werr!("command invalid: {}", advice);
                        break 'l;
                    },
                    ["FAIL", advice, cword, cargs..]
                      if cword == word && cargs == args => {
                        werr!("command failed: {}", advice);
                        break 'l;
                    },
                    _ => ()
                }
            },
            _ => return Err(std::io::IoError {
                kind: std::io::IoErrorKind::OtherIoError,
                desc: "unexpected response",
                detail: None
            })
        }
    }

    Ok(Client{
        request_tx: request_tx,
        response_rx: response_rx
    })
}

fn quit_client(log: &mut Logger, Client{request_tx, ..}: Client)
  -> IoResult<()> {
    log!(log, "Closing client connection");

    request_tx.send(Request::Quit);
    Ok(())
}

/// A one-shot BAPS3 request.
///
/// This takes a server connection and performs the following:
///   - Checks that the server is a BAPS3 server, by seeing if an OHAI line is
///     being received;
///   - Checks the server's FEATURES flags against `features`, and fails if
///     any are missing;
///   - Sends the command `word` `args`;
///   - Reads until the server sends an OKAY, FAIL, or WHAT response for that
///     command.
fn one_shot(log: &mut Logger,
            client: Client,
            features: &[&str],
            word: &str,
            args: &[&str])
  -> IoResult<()> {
    check_baps3(log, client)
      .and_then(|c| check_features(log, features, c))
      .and_then(|c| send_command(log, c, word, args))
      .and_then(|c| quit_client(log, c))
}

/// Creates a Logger from the -v/--verbose flag of a command.
///
/// If verbose is on (-v/--verbose == true), we dump log messages to stderr,
/// else we ignore them.
fn verbose_logger<'a>(verbose: bool) -> Logger<'a> {
    if verbose { |s: &str| { let _ = std::io::stderr().write_line(s); } }
    else       { |_| {} }
}

fn main() {
    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());
    let mut log = verbose_logger(args.flag_verbose);

    Client::new(&*args.flag_target)
      .and_then(|c| one_shot(&mut log,
                             c,
                             &["FileLoad"],
                             "load",
                             &[&*args.arg_file]))
      .unwrap_or_else(|e| werr!("error: {}", e));
}<|endoftext|>


<commit_before>use super::InteractiveBinary;
use ion_shell::{status::*, Value};

use regex::Regex;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Default)]
pub struct IgnoreSetting {
    // Macro definition fails if last flag has a comment at the end of the line.
    /// ignore all commands ("all")
    all: bool,
    /// ignore commands with leading whitespace ("whitespace")
    whitespace: bool,
    /// ignore commands with status code 127 ("no_such_command")
    no_such_command: bool,
    /// used if regexes are defined.
    based_on_regex: bool,
    /// ignore commands that are duplicates
    duplicates: bool,
    // Yes, a bad heap-based Vec, however unfortunately its not possible to store Regex'es in Array
    regexes: Option<Vec<Regex>>,
}

/// Contains all history-related functionality for the `Shell`.
impl<'a> InteractiveBinary<'a> {
    /// Updates the history ignore patterns. Call this whenever HISTORY_IGNORE
    /// is changed.
    pub fn ignore_patterns(&self) -> IgnoreSetting {
        if let Some(Value::Array(patterns)) =
            self.shell.borrow().variables().get_ref("HISTORY_IGNORE")
        {
            let mut settings = IgnoreSetting::default();
            let mut regexes = Vec::new();
            // for convenience and to avoid typos
            let regex_prefix = "regex:";
            for pattern in patterns.into_iter() {
                let pattern = format!("{}", pattern);
                match pattern.as_ref() {
                    "all" => settings.all = true,
                    "no_such_command" => settings.no_such_command = true,
                    "whitespace" => settings.whitespace = true,
                    "duplicates" => settings.duplicates = true,
                    // The length check is there to just ignore empty regex definitions
                    _ if pattern.starts_with(regex_prefix)
                        && pattern.len() > regex_prefix.len() =>
                    {
                        settings.based_on_regex = true;
                        let regex_string = &pattern[regex_prefix.len()..];
                        // We save the compiled regexes, as compiling them can be  an expensive task
                        if let Ok(regex) = Regex::new(regex_string) {
                            regexes.push(regex);
                        }
                    }
                    _ => continue,
                }
            }
            settings.regexes = if !regexes.is_empty() { Some(regexes) } else { None };

            settings
        } else {
            panic!("HISTORY_IGNORE is not set!");
        }
    }

    /// Saves a command in the history, depending on @HISTORY_IGNORE. Should be called
    /// immediately after `on_command()`
    pub fn save_command_in_history(&self, command: &str) {
        if self.should_save_command(command) {
            if self.shell.borrow().variables().get_str("HISTORY_TIMESTAMP").unwrap_or_default()
                == "1"
            {
                // Get current time stamp
                let since_unix_epoch =
                    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
                let cur_time_sys = ["#", &since_unix_epoch.to_owned().to_string()].concat();

                // Push current time to history
                if let Err(err) = self.context.borrow_mut().history.push(cur_time_sys.into()) {
                    eprintln!("ion: {}", err)
                }
            }

            // Push command itself to history
            if let Err(err) = self.context.borrow_mut().history.push(command.into()) {
                eprintln!("ion: {}", err);
            }
        }
    }

    /// Returns true if the given command with the given exit status should be saved in the
    /// history
    fn should_save_command(&self, command: &str) -> bool {
        // just for convenience and to make the code look a bit cleaner
        let ignore = self.ignore_patterns();

        // without the second check the command which sets the local variable would
        // also be ignored. However, this behavior might not be wanted.
        if ignore.all && !command.contains("HISTORY_IGNORE") {
            return false;
        }

        // Here we allow to also ignore the setting of the local variable because we
        // assume the user entered the leading whitespace on purpose.
        if ignore.whitespace && command.chars().next().map_or(false, char::is_whitespace) {
            return false;
        }

        if ignore.no_such_command && self.shell.borrow().previous_status() == NO_SUCH_COMMAND {
            return false;
        }

        if ignore.duplicates {
            self.context.borrow_mut().history.remove_duplicates(command);
        }

        if let Some(ref regexes) = ignore.regexes {
            // ignore command when regex is matched but only if it does not contain
            // "HISTORY_IGNORE", otherwise we would also ignore the command which
            // sets the variable, which could be annoying.
            if regexes.iter().any(|regex| regex.is_match(command))
                && !command.contains("HISTORY_IGNORE")
            {
                return false;
            }
        }

        // default to true, as it's more likely that we want to save a command in
        // history
        true
    }
}
<commit_msg>Remove the option wrapper, it does not serve any purpose<commit_after>use super::InteractiveBinary;
use ion_shell::{status::*, Value};

use regex::Regex;
use std::time::{SystemTime, UNIX_EPOCH};

#[derive(Debug, Default)]
pub struct IgnoreSetting {
    // Macro definition fails if last flag has a comment at the end of the line.
    /// ignore all commands ("all")
    all: bool,
    /// ignore commands with leading whitespace ("whitespace")
    whitespace: bool,
    /// ignore commands with status code 127 ("no_such_command")
    no_such_command: bool,
    /// used if regexes are defined.
    based_on_regex: bool,
    /// ignore commands that are duplicates
    duplicates: bool,
    // Yes, a bad heap-based Vec, however unfortunately its not possible to store Regex'es in Array
    regexes: Vec<Regex>,
}

/// Contains all history-related functionality for the `Shell`.
impl<'a> InteractiveBinary<'a> {
    /// Updates the history ignore patterns. Call this whenever HISTORY_IGNORE
    /// is changed.
    pub fn ignore_patterns(&self) -> IgnoreSetting {
        if let Some(Value::Array(patterns)) =
            self.shell.borrow().variables().get_ref("HISTORY_IGNORE")
        {
            let mut settings = IgnoreSetting::default();
            // for convenience and to avoid typos
            let regex_prefix = "regex:";
            for pattern in patterns.into_iter() {
                let pattern = format!("{}", pattern);
                match pattern.as_ref() {
                    "all" => settings.all = true,
                    "no_such_command" => settings.no_such_command = true,
                    "whitespace" => settings.whitespace = true,
                    "duplicates" => settings.duplicates = true,
                    // The length check is there to just ignore empty regex definitions
                    _ if pattern.starts_with(regex_prefix)
                        && pattern.len() > regex_prefix.len() =>
                    {
                        settings.based_on_regex = true;
                        let regex_string = &pattern[regex_prefix.len()..];
                        // We save the compiled regexes, as compiling them can be  an expensive task
                        if let Ok(regex) = Regex::new(regex_string) {
                            settings.regexes.push(regex);
                        }
                    }
                    _ => continue,
                }
            }

            settings
        } else {
            panic!("HISTORY_IGNORE is not set!");
        }
    }

    /// Saves a command in the history, depending on @HISTORY_IGNORE. Should be called
    /// immediately after `on_command()`
    pub fn save_command_in_history(&self, command: &str) {
        if self.should_save_command(command) {
            if self.shell.borrow().variables().get_str("HISTORY_TIMESTAMP").unwrap_or_default()
                == "1"
            {
                // Get current time stamp
                let since_unix_epoch =
                    SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
                let cur_time_sys = ["#", &since_unix_epoch.to_owned().to_string()].concat();

                // Push current time to history
                if let Err(err) = self.context.borrow_mut().history.push(cur_time_sys.into()) {
                    eprintln!("ion: {}", err)
                }
            }

            // Push command itself to history
            if let Err(err) = self.context.borrow_mut().history.push(command.into()) {
                eprintln!("ion: {}", err);
            }
        }
    }

    /// Returns true if the given command with the given exit status should be saved in the
    /// history
    fn should_save_command(&self, command: &str) -> bool {
        // just for convenience and to make the code look a bit cleaner
        let ignore = self.ignore_patterns();

        // without the second check the command which sets the local variable would
        // also be ignored. However, this behavior might not be wanted.
        if ignore.all && !command.contains("HISTORY_IGNORE") {
            return false;
        }

        // Here we allow to also ignore the setting of the local variable because we
        // assume the user entered the leading whitespace on purpose.
        if ignore.whitespace && command.chars().next().map_or(false, char::is_whitespace) {
            return false;
        }

        if ignore.no_such_command && self.shell.borrow().previous_status() == NO_SUCH_COMMAND {
            return false;
        }

        if ignore.duplicates {
            self.context.borrow_mut().history.remove_duplicates(command);
        }

        // ignore command when regex is matched but only if it does not contain
        // "HISTORY_IGNORE", otherwise we would also ignore the command which
        // sets the variable, which could be annoying.
        if !command.contains("HISTORY_IGNORE")
            && ignore.regexes.iter().any(|regex| regex.is_match(command))
        {
            return false;
        }

        // default to true, as it's more likely that we want to save a command in
        // history
        true
    }
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};
use std::slice;

use cairo::prelude::SurfaceExt;
use cairo::{self, ImageSurface};
use cairo_sys;
use libc::c_char;

use attributes::Attribute;
use error::{AttributeError, NodeError};
use handle::RsvgHandle;
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::{self, parse, Parse};
use property_bag::PropertyBag;
use srgb::{linearize_surface, unlinearize_surface};
use util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::{get_surface, Filter, FilterError, PrimitiveWithInput};

/// Enumeration of the possible compositing operations.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Operator {
    Over,
    In,
    Out,
    Atop,
    Xor,
    Arithmetic,
}

/// The `feComposite` filter primitive.
struct Composite {
    base: PrimitiveWithInput,
    in2: RefCell<Option<Input>>,
    operator: Cell<Operator>,
    k1: Cell<f64>,
    k2: Cell<f64>,
    k3: Cell<f64>,
    k4: Cell<f64>,
}

impl Composite {
    /// Constructs a new `Composite` with empty properties.
    #[inline]
    fn new() -> Composite {
        Composite {
            base: PrimitiveWithInput::new::<Self>(),
            in2: RefCell::new(None),
            operator: Cell::new(Operator::Over),
            k1: Cell::new(0f64),
            k2: Cell::new(0f64),
            k3: Cell::new(0f64),
            k4: Cell::new(0f64),
        }
    }
}

impl NodeTrait for Composite {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::In2 => {
                    self.in2.replace(Some(parse("in2", value, (), None)?));
                }
                Attribute::Operator => self.operator.set(parse("operator", value, (), None)?),
                Attribute::K1 => self
                    .k1
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K2 => self
                    .k2
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K3 => self
                    .k3
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K4 => self
                    .k4
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                _ => (),
            }
        }

        Ok(())
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl Filter for Composite {
    fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
        let bounds = self.base.get_bounds(ctx);

        let input_surface = get_surface(self.base.get_input(ctx))?;
        let input_2_surface = get_surface(ctx.get_input(self.in2.borrow().as_ref()))?;

        // It's important to linearize sRGB before doing any blending, since otherwise the colors
        // will be darker than they should be.
        let input_surface =
            linearize_surface(&input_surface, bounds).map_err(FilterError::BadInputSurfaceStatus)?;

        let width = input_surface.get_width();
        let height = input_surface.get_height();
        let input_stride = input_surface.get_stride();
        let input_2_stride = input_2_surface.get_stride();

        let output_surface = if self.operator.get() == Operator::Arithmetic {
            // TODO: this currently gives "non-exclusive access" (can we make read-only borrows?)
            // let input_data = input_surface.get_data().unwrap();
            input_surface.flush();
            if input_surface.status() != cairo::Status::Success {
                return Err(FilterError::BadInputSurfaceStatus(input_surface.status()));
            }
            let input_data_ptr =
                unsafe { cairo_sys::cairo_image_surface_get_data(input_surface.to_raw_none()) };
            assert!(!input_data_ptr.is_null());
            let input_data_len = input_stride as usize * height as usize;
            let input_data = unsafe { slice::from_raw_parts(input_data_ptr, input_data_len) };

            let input_2_surface = linearize_surface(&input_2_surface, bounds)
                .map_err(FilterError::BadInputSurfaceStatus)?;

            input_2_surface.flush();
            if input_2_surface.status() != cairo::Status::Success {
                return Err(FilterError::BadInputSurfaceStatus(input_2_surface.status()));
            }
            let input_2_data_ptr =
                unsafe { cairo_sys::cairo_image_surface_get_data(input_2_surface.to_raw_none()) };
            assert!(!input_2_data_ptr.is_null());
            let input_2_data_len = input_2_stride as usize * height as usize;
            let input_2_data = unsafe { slice::from_raw_parts(input_2_data_ptr, input_2_data_len) };

            let mut output_surface = ImageSurface::create(cairo::Format::ARgb32, width, height)
                .map_err(FilterError::OutputSurfaceCreation)?;

            let output_stride = output_surface.get_stride();
            {
                let mut output_data = output_surface.get_data().unwrap();

                let k1 = self.k1.get();
                let k2 = self.k2.get();
                let k3 = self.k3.get();
                let k4 = self.k4.get();

                for y in bounds.y0..bounds.y1 {
                    for x in bounds.x0..bounds.x1 {
                        let i1a =
                            f64::from(input_data[(y * input_stride + 4 * x + 3) as usize]) / 255f64;
                        let i2a = f64::from(input_2_data[(y * input_2_stride + 4 * x + 3) as usize])
                            / 255f64;
                        let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
                        let oa = clamp(oa, 0f64, 1f64);

                        // Contents of image surfaces are transparent by default, so if the
                        // resulting pixel is transparent there's no need
                        // to do anything.
                        if oa > 0f64 {
                            output_data[(y * output_stride + 4 * x + 3) as usize] =
                                (oa * 255f64).round() as u8;

                            for ch in 0..3 {
                                let i1 = f64::from(
                                    input_data[(y * input_stride + 4 * x + ch) as usize],
                                ) / 255f64;
                                let i2 = f64::from(
                                    input_2_data[(y * input_2_stride + 4 * x + ch) as usize],
                                ) / 255f64;

                                let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
                                let o = clamp(o, 0f64, oa);

                                let o = (o * 255f64).round() as u8;
                                output_data[(y * output_stride + 4 * x + ch) as usize] = o;
                            }
                        }
                    }
                }
            }

            output_surface
        } else {
            let output_surface = linearize_surface(&input_2_surface, bounds)
                .map_err(FilterError::BadInputSurfaceStatus)?;

            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            cr.set_source_surface(&input_surface, 0f64, 0f64);
            cr.set_operator(self.operator.get().into());
            cr.paint();

            output_surface
        };

        let output_surface = unlinearize_surface(&output_surface, bounds)
            .map_err(FilterError::OutputSurfaceCreation)?;

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }
}

impl Parse for Operator {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _data: Self::Data) -> Result<Self, Self::Err> {
        match s {
            "over" => Ok(Operator::Over),
            "in" => Ok(Operator::In),
            "out" => Ok(Operator::Out),
            "atop" => Ok(Operator::Atop),
            "xor" => Ok(Operator::Xor),
            "arithmetic" => Ok(Operator::Arithmetic),
            _ => Err(AttributeError::Value("invalid operator value".to_string())),
        }
    }
}

impl From<Operator> for cairo::Operator {
    #[inline]
    fn from(x: Operator) -> Self {
        match x {
            Operator::Over => cairo::Operator::Over,
            Operator::In => cairo::Operator::In,
            Operator::Out => cairo::Operator::Out,
            Operator::Atop => cairo::Operator::Atop,
            Operator::Xor => cairo::Operator::Xor,
            _ => panic!("can't convert Operator::Arithmetic to a cairo::Operator"),
        }
    }
}

/// Returns a new `feComposite` node.
#[no_mangle]
pub unsafe extern "C" fn rsvg_new_filter_primitive_composite(
    _element_name: *const c_char,
    parent: *mut RsvgNode,
) -> *mut RsvgNode {
    let filter = Composite::new();
    boxed_node_new(NodeType::FilterPrimitiveComposite, parent, Box::new(filter))
}
<commit_msg>Refactor feComposite to use the new iterator<commit_after>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface};
use libc::c_char;

use attributes::Attribute;
use error::{AttributeError, NodeError};
use handle::RsvgHandle;
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::{self, parse, Parse};
use property_bag::PropertyBag;
use srgb::{linearize_surface, unlinearize_surface};
use util::clamp;

use super::context::{FilterContext, FilterOutput, FilterResult};
use super::input::Input;
use super::iterators::{ImageSurfaceDataShared, Pixels};
use super::{get_surface, Filter, FilterError, PrimitiveWithInput};

/// Enumeration of the possible compositing operations.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum Operator {
    Over,
    In,
    Out,
    Atop,
    Xor,
    Arithmetic,
}

/// The `feComposite` filter primitive.
struct Composite {
    base: PrimitiveWithInput,
    in2: RefCell<Option<Input>>,
    operator: Cell<Operator>,
    k1: Cell<f64>,
    k2: Cell<f64>,
    k3: Cell<f64>,
    k4: Cell<f64>,
}

impl Composite {
    /// Constructs a new `Composite` with empty properties.
    #[inline]
    fn new() -> Composite {
        Composite {
            base: PrimitiveWithInput::new::<Self>(),
            in2: RefCell::new(None),
            operator: Cell::new(Operator::Over),
            k1: Cell::new(0f64),
            k2: Cell::new(0f64),
            k3: Cell::new(0f64),
            k4: Cell::new(0f64),
        }
    }
}

impl NodeTrait for Composite {
    fn set_atts(
        &self,
        node: &RsvgNode,
        handle: *const RsvgHandle,
        pbag: &PropertyBag,
    ) -> NodeResult {
        self.base.set_atts(node, handle, pbag)?;

        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::In2 => {
                    self.in2.replace(Some(parse("in2", value, (), None)?));
                }
                Attribute::Operator => self.operator.set(parse("operator", value, (), None)?),
                Attribute::K1 => self
                    .k1
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K2 => self
                    .k2
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K3 => self
                    .k3
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                Attribute::K4 => self
                    .k4
                    .set(parsers::number(value).map_err(|err| NodeError::parse_error(attr, err))?),
                _ => (),
            }
        }

        Ok(())
    }

    #[inline]
    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        self.base.get_c_impl()
    }
}

impl Filter for Composite {
    fn render(&self, _node: &RsvgNode, ctx: &FilterContext) -> Result<FilterResult, FilterError> {
        let bounds = self.base.get_bounds(ctx);

        let input_surface = get_surface(self.base.get_input(ctx))?;
        let input_2_surface = get_surface(ctx.get_input(self.in2.borrow().as_ref()))?;

        // It's important to linearize sRGB before doing any blending, since otherwise the colors
        // will be darker than they should be.
        let input_surface =
            linearize_surface(&input_surface, bounds).map_err(FilterError::BadInputSurfaceStatus)?;

        let output_surface = if self.operator.get() == Operator::Arithmetic {
            let input_data = ImageSurfaceDataShared::new(&input_surface)?;
            let input_2_data = ImageSurfaceDataShared::new(&input_2_surface)?;

            let mut output_surface = ImageSurface::create(
                cairo::Format::ARgb32,
                input_data.width as i32,
                input_data.height as i32,
            ).map_err(FilterError::OutputSurfaceCreation)?;

            let output_stride = output_surface.get_stride() as usize;
            {
                let mut output_data = output_surface.get_data().unwrap();

                let k1 = self.k1.get();
                let k2 = self.k2.get();
                let k3 = self.k3.get();
                let k4 = self.k4.get();

                for (x, y, pixel, pixel_2) in Pixels::new(input_data, bounds)
                    .map(|(x, y, p)| (x, y, p, input_2_data.get_pixel(x, y)))
                {
                    let i1a = f64::from(pixel.a) / 255f64;
                    let i2a = f64::from(pixel_2.a) / 255f64;
                    let oa = k1 * i1a * i2a + k2 * i1a + k3 * i2a + k4;
                    let oa = clamp(oa, 0f64, 1f64);

                    let output_base = y * output_stride + 4 * x;

                    // Contents of image surfaces are transparent by default, so if the
                    // resulting pixel is transparent there's no need
                    // to do anything.
                    if oa > 0f64 {
                        output_data[output_base + 3] = (oa * 255f64).round() as u8;

                        // TODO: make this much better with mutable pixel iterators for output.
                        for (ch, &(i1, i2)) in [
                            (pixel.r, pixel_2.r),
                            (pixel.g, pixel_2.g),
                            (pixel.b, pixel_2.b),
                        ].iter()
                            .enumerate()
                        {
                            let i1 = f64::from(i1) / 255f64;
                            let i2 = f64::from(i2) / 255f64;

                            let o = k1 * i1 * i2 + k2 * i1 + k3 * i2 + k4;
                            let o = clamp(o, 0f64, oa);

                            let o = (o * 255f64).round() as u8;
                            output_data[output_base + ch] = o;
                        }
                    }
                }
            }

            output_surface
        } else {
            let output_surface = linearize_surface(&input_2_surface, bounds)
                .map_err(FilterError::BadInputSurfaceStatus)?;

            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                bounds.x0 as f64,
                bounds.y0 as f64,
                (bounds.x1 - bounds.x0) as f64,
                (bounds.y1 - bounds.y0) as f64,
            );
            cr.clip();

            cr.set_source_surface(&input_surface, 0f64, 0f64);
            cr.set_operator(self.operator.get().into());
            cr.paint();

            output_surface
        };

        let output_surface = unlinearize_surface(&output_surface, bounds)
            .map_err(FilterError::OutputSurfaceCreation)?;

        Ok(FilterResult {
            name: self.base.result.borrow().clone(),
            output: FilterOutput {
                surface: output_surface,
                bounds,
            },
        })
    }
}

impl Parse for Operator {
    type Data = ();
    type Err = AttributeError;

    fn parse(s: &str, _data: Self::Data) -> Result<Self, Self::Err> {
        match s {
            "over" => Ok(Operator::Over),
            "in" => Ok(Operator::In),
            "out" => Ok(Operator::Out),
            "atop" => Ok(Operator::Atop),
            "xor" => Ok(Operator::Xor),
            "arithmetic" => Ok(Operator::Arithmetic),
            _ => Err(AttributeError::Value("invalid operator value".to_string())),
        }
    }
}

impl From<Operator> for cairo::Operator {
    #[inline]
    fn from(x: Operator) -> Self {
        match x {
            Operator::Over => cairo::Operator::Over,
            Operator::In => cairo::Operator::In,
            Operator::Out => cairo::Operator::Out,
            Operator::Atop => cairo::Operator::Atop,
            Operator::Xor => cairo::Operator::Xor,
            _ => panic!("can't convert Operator::Arithmetic to a cairo::Operator"),
        }
    }
}

/// Returns a new `feComposite` node.
#[no_mangle]
pub unsafe extern "C" fn rsvg_new_filter_primitive_composite(
    _element_name: *const c_char,
    parent: *mut RsvgNode,
) -> *mut RsvgNode {
    let filter = Composite::new();
    boxed_node_new(NodeType::FilterPrimitiveComposite, parent, Box::new(filter))
}
<|endoftext|>