<commit_before>use std::process::Command;
use std::sync::mpsc::{channel, Receiver, Sender};

use threadpool::ThreadPool;

/// Runs child processes and provides a channel to asynchronously wait on
/// their completion.
///
/// This enables us to remain responsive to interruption requests even if
/// the child process does not honor our kill requests.
pub struct Runner {
    pool: ThreadPool,
    tx: Sender<()>,
}

impl Runner {
    pub fn new() -> (Runner, Receiver<()>) {
        let (tx, rx) = channel();
        (Runner {
            pool: ThreadPool::new(1),
            tx: tx,
        },
         rx)
    }

    #[cfg(target_family = "windows")]
    pub fn clear_screen(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    pub fn clear_screen(&self) {
        let _ = Command::new("clear").status();
    }

    pub fn run_command(&mut self,
                       cmd: &str,
                       updated_paths: Vec<&str>)
                       -> Option<Process> {
        let child = Process::new(cmd, updated_paths);

        if let Some(ref process) = child {
            let tx = self.tx.clone();
            let mut p = process.as_platform_process();

            self.pool.execute(move || {
                p.wait();

                let _ = tx.send(());
            });
        }

        child
    }
}

/// High-level wrapper around a child process
/// Unlike `platform::Process`, `Process` kills the child when it is dropped.
pub struct Process {
    process: platform::Process
}

impl Process {
    pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
        platform::Process::new(cmd, updated_paths).and_then(|p| {
            Some(Process { process: p })
        })
    }

    fn as_platform_process(&self) -> platform::Process {
        self.process.clone()
    }

    pub fn kill(&mut self) {
        self.process.kill();
    }

    #[allow(dead_code)]
    pub fn wait(&mut self) {
        self.process.wait();
    }
}

impl Drop for Process {
    fn drop(&mut self) {
        self.kill();
    }
}

#[cfg(target_family = "unix")]
mod platform {
    use std::process::Command;

    #[derive(Clone)]
    pub struct Process {
        child_pid: i32,
    }

    #[cfg(target_family = "unix")]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
            use libc;
            use std::os::unix::process::CommandExt;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if !updated_paths.is_empty() {
                command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
            }

            let c = command.before_exec(|| unsafe {
                    libc::setpgid(0, 0);
                    Ok(())
                })
                .spawn()
                .ok();

            match c {
                Some(process) => {
                    Some(Process {
                        child_pid: process.id() as i32,
                    })
                }
                None => None,
            }
        }

        pub fn kill(&mut self) {
            use libc;

            extern "C" {
                fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
            }

            unsafe {
                killpg(self.child_pid, libc::SIGTERM);
            }
        }

        pub fn wait(&mut self) {
            use nix::sys::wait::waitpid;

            let _ = waitpid(-self.child_pid, None);
        }
    }
}

#[cfg(target_family = "windows")]
mod platform {
    use std::process::Command;
    use winapi::winnt::HANDLE;

    #[derive(Clone)]
    pub struct Process {
        child_handle: HANDLE,
    }

    unsafe impl Send for Process {}

    #[cfg(target_family = "windows")]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
            use std::os::windows::io::AsRawHandle;

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if !updated_paths.is_empty() {
                command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
            }

            match command.spawn().ok() {
                Some(process) => Some(Process { child_handle: process.as_raw_handle() }),
                None => None,
            }
        }

        pub fn kill(&mut self) {
            use kernel32::TerminateProcess;

            unsafe {
                let _ = TerminateProcess(self.child_handle, 0);
            }
        }

        pub fn wait(&mut self) {
            use kernel32::WaitForSingleObject;
            use winapi::winbase::INFINITE;

            unsafe {
                let _ = WaitForSingleObject(self.child_handle, INFINITE);
            }
        }
    }
}

#[cfg(test)]
mod process_tests {
    use std::path::Path;
    use std::thread;
    use std::time::Duration;

    use mktemp::Temp;

    use super::Process;

    fn file_contents(path: &Path) -> String {
        use std::fs::File;
        use std::io::Read;

        let mut f = File::open(path).unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        s
    }

    #[test]
    #[cfg(target_family = "unix")]
    fn test_start() {
        let process = Process::new("echo hi", vec![]);

        assert!(process.is_some());
    }

    #[test]
    #[cfg(target_family = "unix")]
    fn test_wait() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();
        let mut process = Process::new(&format!("echo hi > {}", path.to_str().unwrap()), vec![]).unwrap();
        process.wait();

        assert!(file_contents(&path).starts_with("hi"));
    }

    #[test]
    #[cfg(target_family = "unix")]
    fn test_kill() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();

        let mut process = Process::new(&format!("sleep 20; echo hi > {}", path.to_str().unwrap()), vec![]).unwrap();
        thread::sleep(Duration::from_millis(250));
        process.kill();
        process.wait();

        assert!(file_contents(&path) == "");
    }
}
<commit_msg>Fix unused import warnings<commit_after>use std::process::Command;
use std::sync::mpsc::{channel, Receiver, Sender};

use threadpool::ThreadPool;

/// Runs child processes and provides a channel to asynchronously wait on
/// their completion.
///
/// This enables us to remain responsive to interruption requests even if
/// the child process does not honor our kill requests.
pub struct Runner {
    pool: ThreadPool,
    tx: Sender<()>,
}

impl Runner {
    pub fn new() -> (Runner, Receiver<()>) {
        let (tx, rx) = channel();
        (Runner {
            pool: ThreadPool::new(1),
            tx: tx,
        },
         rx)
    }

    #[cfg(target_family = "windows")]
    pub fn clear_screen(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    pub fn clear_screen(&self) {
        let _ = Command::new("clear").status();
    }

    pub fn run_command(&mut self,
                       cmd: &str,
                       updated_paths: Vec<&str>)
                       -> Option<Process> {
        let child = Process::new(cmd, updated_paths);

        if let Some(ref process) = child {
            let tx = self.tx.clone();
            let mut p = process.as_platform_process();

            self.pool.execute(move || {
                p.wait();

                let _ = tx.send(());
            });
        }

        child
    }
}

/// High-level wrapper around a child process
/// Unlike `platform::Process`, `Process` kills the child when it is dropped.
pub struct Process {
    process: platform::Process
}

impl Process {
    pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
        platform::Process::new(cmd, updated_paths).and_then(|p| {
            Some(Process { process: p })
        })
    }

    fn as_platform_process(&self) -> platform::Process {
        self.process.clone()
    }

    pub fn kill(&mut self) {
        self.process.kill();
    }

    #[allow(dead_code)]
    pub fn wait(&mut self) {
        self.process.wait();
    }
}

impl Drop for Process {
    fn drop(&mut self) {
        self.kill();
    }
}

#[cfg(target_family = "unix")]
mod platform {
    use std::process::Command;

    #[derive(Clone)]
    pub struct Process {
        child_pid: i32,
    }

    #[cfg(target_family = "unix")]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
            use libc;
            use std::os::unix::process::CommandExt;

            let mut command = Command::new("sh");
            command.arg("-c").arg(cmd);

            if !updated_paths.is_empty() {
                command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
            }

            let c = command.before_exec(|| unsafe {
                    libc::setpgid(0, 0);
                    Ok(())
                })
                .spawn()
                .ok();

            match c {
                Some(process) => {
                    Some(Process {
                        child_pid: process.id() as i32,
                    })
                }
                None => None,
            }
        }

        pub fn kill(&mut self) {
            use libc;

            extern "C" {
                fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
            }

            unsafe {
                killpg(self.child_pid, libc::SIGTERM);
            }
        }

        pub fn wait(&mut self) {
            use nix::sys::wait::waitpid;

            let _ = waitpid(-self.child_pid, None);
        }
    }
}

#[cfg(target_family = "windows")]
mod platform {
    use std::process::Command;
    use winapi::winnt::HANDLE;

    #[derive(Clone)]
    pub struct Process {
        child_handle: HANDLE,
    }

    unsafe impl Send for Process {}

    #[cfg(target_family = "windows")]
    impl Process {
        pub fn new(cmd: &str, updated_paths: Vec<&str>) -> Option<Process> {
            use std::os::windows::io::AsRawHandle;

            let mut command = Command::new("cmd.exe");
            command.arg("/C").arg(cmd);

            if !updated_paths.is_empty() {
                command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
            }

            match command.spawn().ok() {
                Some(process) => Some(Process { child_handle: process.as_raw_handle() }),
                None => None,
            }
        }

        pub fn kill(&mut self) {
            use kernel32::TerminateProcess;

            unsafe {
                let _ = TerminateProcess(self.child_handle, 0);
            }
        }

        pub fn wait(&mut self) {
            use kernel32::WaitForSingleObject;
            use winapi::winbase::INFINITE;

            unsafe {
                let _ = WaitForSingleObject(self.child_handle, INFINITE);
            }
        }
    }
}

#[cfg(test)]
#[cfg(target_family = "unix")]
mod process_tests {
    use std::path::Path;
    use std::thread;
    use std::time::Duration;

    use mktemp::Temp;

    use super::Process;

    fn file_contents(path: &Path) -> String {
        use std::fs::File;
        use std::io::Read;

        let mut f = File::open(path).unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        s
    }

    #[test]
    fn test_start() {
        let process = Process::new("echo hi", vec![]);

        assert!(process.is_some());
    }

    #[test]
    fn test_wait() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();
        let mut process = Process::new(&format!("echo hi > {}", path.to_str().unwrap()), vec![]).unwrap();
        process.wait();

        assert!(file_contents(&path).starts_with("hi"));
    }

    #[test]
    fn test_kill() {
        let file = Temp::new_file().unwrap();
        let path = file.to_path_buf();

        let mut process = Process::new(&format!("sleep 20; echo hi > {}", path.to_str().unwrap()), vec![]).unwrap();
        thread::sleep(Duration::from_millis(250));
        process.kill();
        process.wait();

        assert!(file_contents(&path) == "");
    }
}
<|endoftext|>


<commit_before>use std::process::{Child, Command};

pub struct Runner {
    process: Option<Child>,
    restart: bool,
    cls: bool,
}

impl Runner {
    pub fn new(restart: bool, clear: bool) -> Runner {
        Runner {
            process: None,
            restart: restart,
            cls: clear
        }
    }

    #[cfg(target_family = "windows")]
    fn clear(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    fn clear(&self) {
        let _ = Command::new("clear").status();
    }

    #[cfg(target_family = "windows")]
    fn kill(child: &mut Child) {
        let _ = child.kill();
    }

    #[cfg(target_family = "unix")]
    fn kill(child: &mut Child) {
        use libc;

        extern {
            fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
        }

        unsafe {
            killpg(child.id() as i32, libc::SIGTERM);
        }
    }

    #[cfg(target_family = "windows")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        let mut command = Command::new("cmd.exe");
        command.arg("/C").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command.spawn().ok()
    }

    #[cfg(target_family = "unix")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        use libc;
        use std::os::unix::process::CommandExt;

        let mut command = Command::new("sh");
        command.arg("-c").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        command
            .before_exec(|| unsafe { libc::setpgid(0, 0); Ok(()) })
            .spawn()
            .ok()
    }

    pub fn run_command(&mut self, cmd: &str, updated_paths: Vec<&str>) {
        if let Some(ref mut child) = self.process {
            if self.restart {
                debug!("Killing child process (pid: {})", child.id());
                Runner::kill(child);
            }

            debug!("Waiting for child process (pid: {})", child.id());
            Runner::wait(child);
        }

        if self.cls {
            self.clear();
        }

        debug!("Executing: {}", cmd);

        self.process = self.invoke(cmd, updated_paths);
    }

    #[cfg(target_family = "windows")]
    fn wait(child: &mut Child) {
        let _ = child.wait();
    }

    #[cfg(target_family = "unix")]
    fn wait(child: &mut Child) {
        use libc;

        unsafe {
            let pid = child.id() as i32;
            let status: Box<i32> = Box::new(0);
            libc::waitpid(-pid, Box::into_raw(status), 0);
        }
    }

}
<commit_msg>Refactor Runner a bit; add Drop impl for eventual use<commit_after>use std::process::{Child, Command};

pub struct Runner {
    process: Option<Child>,
    restart: bool,
    cls: bool,
}

impl Runner {
    pub fn new(restart: bool, clear: bool) -> Runner {
        Runner {
            process: None,
            restart: restart,
            cls: clear
        }
    }

    #[cfg(target_family = "windows")]
    fn clear(&self) {
        let _ = Command::new("cls").status();
    }

    #[cfg(target_family = "unix")]
    fn clear(&self) {
        let _ = Command::new("clear").status();
    }

    #[cfg(target_family = "windows")]
    fn kill(&self) {
        if let Some(ref child) = self.process {
            debug!("Killing child process (pid: {})", child.id());

            let _ = child.kill();
        }
    }

    #[cfg(target_family = "unix")]
    fn kill(&self) {
        use libc;

        extern {
            fn killpg(pgrp: libc::pid_t, sig: libc::c_int) -> libc::c_int;
        }

        if let Some(ref child) = self.process {
            debug!("Killing child process (pid: {})", child.id());

            unsafe {
                killpg(child.id() as i32, libc::SIGTERM);
            }
        }
    }

    #[cfg(target_family = "windows")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        let mut command = Command::new("cmd.exe");
        command.arg("/C").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        debug!("Executing: {}", cmd);

        command.spawn().ok()
    }

    #[cfg(target_family = "unix")]
    fn invoke(&self, cmd: &str, updated_paths: Vec<&str>) -> Option<Child> {
        use libc;
        use std::os::unix::process::CommandExt;

        let mut command = Command::new("sh");
        command.arg("-c").arg(cmd);

        if !updated_paths.is_empty() {
            command.env("WATCHEXEC_UPDATED_PATH", updated_paths[0]);
        }

        debug!("Executing: {}", cmd);

        command
            .before_exec(|| unsafe { libc::setpgid(0, 0); Ok(()) })
            .spawn()
            .ok()
    }

    pub fn run_command(&mut self, cmd: &str, updated_paths: Vec<&str>) {
        if self.restart {
            self.kill();
        }

        self.wait();

        if self.cls {
            self.clear();
        }

        self.process = self.invoke(cmd, updated_paths);
    }

    #[cfg(target_family = "windows")]
    fn wait(&self) {
        if let Some(ref child) = self.process {
            debug!("Waiting for child process (pid: {})", child.id());
            let _ = child.wait();
        }
    }

    #[cfg(target_family = "unix")]
    fn wait(&self) {
        use nix::sys::wait::{waitpid};

        if let Some(ref child) = self.process {
            debug!("Waiting for child process (pid: {})", child.id());

            let pid = child.id() as i32;
            let _ = waitpid(-pid, None);
        }
    }
}

impl Drop for Runner {
    fn drop(&mut self) {
        self.kill();
        self.wait();
    }
}
<|endoftext|>


<commit_before>// Claxon -- A FLAC decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::cmp::Eq;
use std::ops::{Add, BitAnd, BitOr, Neg, Shl, Shr, Sub};

/// An trait that allows for interegers to be generic in width.
pub trait Sample: Copy + Clone + Eq +
    Neg<Output = Self> +
    Add<Output = Self> +
    Sub<Output = Self> +
    Shl<usize, Output = Self> +
    Shr<usize, Output = Self> +
    BitOr<Self, Output = Self> +
    BitAnd<Self, Output = Self> {

    type Unsigned: BitAnd<<Self as Sample>::Unsigned,
                          Output = <Self as Sample>::Unsigned>
                 + BitOr<Output = <Self as Sample>::Unsigned>
                 + Shl<usize, Output = <Self as Sample>::Unsigned>
                 + Shr<usize, Output = <Self as Sample>::Unsigned>
                 + Add<Output = <Self as Sample>::Unsigned>
                 + Eq + Copy + Clone;

    /// Returns the maximal value that the type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn max() -> Self;

    /// Returns the minimal value that the type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn min() -> Self;

    /// Returns the maximal value that the `Unsigned` type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn max_unsigned() -> <Self as Sample>::Unsigned;

    /// Returns 0.
    // TODO: could be an associated constant once those land.
    fn zero() -> Self {
        Self::from_i8(0).unwrap()
    }

    /// Returns 1.
    // TODO: could be an associated constant once those land.
    fn one() -> Self {
        Self::from_i8(1).unwrap()
    }

    /// Returns 0 as the unsigned type.
    // TODO: could be an associated constant once those land.
    fn zero_unsigned() -> <Self as Sample>::Unsigned {
        <<Self as Sample>::Unsigned as FromPrimitive>::from_i8(0).unwrap()
    }

    /// Returns 1 as the unsigned type.
    // TODO: could be an associated constant once those land.
    fn one_unsigned() -> <Self as Sample>::Unsigned {
        <<Self as Sample>::Unsigned as FromPrimitive>::from_i8(1).unwrap()
    }

    /// Interprets the unsigned value as a signed number.
    fn from_unsigned(unsigned: <Self as Sample>::Unsigned) -> Self;

    /// Converts an `i32` to the sample, assuming it will not overflow.
    fn from_i32_nofail(x: i32) -> Self;

    /// Converts an `i32` to the sample, returning `None` on overflow.
    fn from_i32(x: i32) -> Option<Self>;

    /// Adds with wraparound on overflow.
    fn wrapping_add(self, other: Self) -> Self;

    /// Subtracts with wraparound on overflow.
    fn wrapping_sub(self, other: Self) -> Self;
}

impl Sample for i8 {

    type Unsigned = u8;

    fn max() -> i8 {
        use std::i8;
        i8::MAX
    }

    fn min() -> i8 {
        use std::i8;
        i8::MIN
    }

    fn max_unsigned() -> u8 {
        use std::u8;
        u8::MAX
    }

    fn from_unsigned(unsigned: u8) -> i8 {
        unsigned as i8
    }

    fn from_i32_nofail(x: i32) -> i8 {
        x as i8
    }

    fn from_i32(x: i32) -> Option<i8> {
        use std::i8;
        if x > i8::MAX || x < i8::MIN {
            None
        } else {
            x as i8
        }
    }

    fn wrapping_add(self, other: i8) -> i8 {
        self.wrapping_add(other)
    }

    fn wrapping_sub(self, other: i8) -> i8 {
        self.wrapping_sub(other)
    }
}

impl Sample for i16 {

    type Unsigned = u16;

    fn max() -> i16 {
        use std::i16;
        i16::MAX
    }

    fn min() -> i16 {
        use std::i16;
        i16::MIN
    }

    fn max_unsigned() -> u16 {
        use std::u16;
        u16::MAX
    }

    fn from_unsigned(unsigned: u16) -> i16 {
        unsigned as i16
    }

    fn from_i32_nofail(x: i32) -> i16 {
        x as i16
    }

    fn from_i32(x: i32) -> Option<i16> {
        use std::i16;
        if x > i16::MAX || x < i16::MIN {
            None
        } else {
            x as i16
        }
    }

    fn wrapping_add(self, other: i16) -> i16 {
        self.wrapping_add(other)
    }

    fn wrapping_sub(self, other: i16) -> i16 {
        self.wrapping_sub(other)
    }
}

impl Sample for i32 {

    type Unsigned = u32;

    fn max() -> i32 {
        use std::i32;
        i32::MAX
    }

    fn min() -> i32 {
        use std::i32;
        i32::MIN
    }

    fn max_unsigned() -> u32 {
        use std::u32;
        u32::MAX
    }

    fn from_unsigned(unsigned: u32) -> i32 {
        unsigned as i32
    }

    fn from_i32_nofail(x: i32) -> i32 {
        x
    }

    fn from_i32(x: i32) -> Option<i32> {
        use std::i32;
        if x > i32::MAX || x < i32::MIN {
            None
        } else {
            x as i32
        }
    }

    fn wrapping_add(self, other: i32) -> i32 {
        self.wrapping_add(other)
    }

    fn wrapping_sub(self, other: i32) -> i32 {
        self.wrapping_sub(other)
    }
}
<commit_msg>replace manual Sample impls by a macro<commit_after>// Claxon -- A FLAC decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License, version 3,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::cmp::Eq;
use std::ops::{Add, BitAnd, BitOr, Neg, Shl, Shr, Sub};

/// An trait that allows for interegers to be generic in width.
pub trait Sample: Copy + Clone + Eq +
    Neg<Output = Self> +
    Add<Output = Self> +
    Sub<Output = Self> +
    Shl<usize, Output = Self> +
    Shr<usize, Output = Self> +
    BitOr<Self, Output = Self> +
    BitAnd<Self, Output = Self> {

    type Unsigned: BitAnd<<Self as Sample>::Unsigned,
                          Output = <Self as Sample>::Unsigned>
                 + BitOr<Output = <Self as Sample>::Unsigned>
                 + Shl<usize, Output = <Self as Sample>::Unsigned>
                 + Shr<usize, Output = <Self as Sample>::Unsigned>
                 + Add<Output = <Self as Sample>::Unsigned>
                 + Eq + Copy + Clone;

    /// Returns the maximal value that the type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn max() -> Self;

    /// Returns the minimal value that the type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn min() -> Self;

    /// Returns the maximal value that the `Unsigned` type can contain.
    // TODO: is this actually required, can we do without in non-debug versions?
    fn max_unsigned() -> <Self as Sample>::Unsigned;

    /// Returns 0.
    // TODO: could be an associated constant once those land.
    fn zero() -> Self;

    /// Returns 1.
    // TODO: could be an associated constant once those land.
    fn one() -> Self;

    /// Returns 0 as the unsigned type.
    // TODO: could be an associated constant once those land.
    fn zero_unsigned() -> <Self as Sample>::Unsigned;

    /// Returns 1 as the unsigned type.
    // TODO: could be an associated constant once those land.
    fn one_unsigned() -> <Self as Sample>::Unsigned;

    /// Interprets the unsigned value as a signed number.
    fn from_unsigned(unsigned: <Self as Sample>::Unsigned) -> Self;

    /// Converts an `i32` to the sample, assuming it will not overflow.
    fn from_i32_nofail(x: i32) -> Self;

    /// Converts an `i32` to the sample, returning `None` on overflow.
    fn from_i32(x: i32) -> Option<Self>;

    /// Adds with wraparound on overflow.
    fn wrapping_add(self, other: Self) -> Self;

    /// Subtracts with wraparound on overflow.
    fn wrapping_sub(self, other: Self) -> Self;
}

macro_rules! impl_sample {
    ($signed: ident, $unsigned: ident) => {
        impl Sample for $signed {

            type Unsigned = $unsigned;

            fn max() -> $signed {
                use std::$signed;
                $signed::MAX
            }

            fn min() -> $signed {
                use std::$signed;
                $signed::MIN
            }

            fn max_unsigned() -> $unsigned {
                use std::$unsigned;
                $unsigned::MAX
            }

            fn zero() -> $signed {
                0
            }

            fn one() -> $signed {
                1
            }

            fn zero_unsigned() -> $unsigned {
                0
            }

            fn one_unsigned() -> $unsigned {
                1
            }

            fn from_unsigned(unsigned: $unsigned) -> $signed {
                unsigned as $signed
            }

            fn from_i32_nofail(x: i32) -> $signed {
                x as $signed
            }

            fn from_i32(x: i32) -> Option<$signed> {
                use std::$signed;
                if x > $signed::MAX || x < $signed::MIN {
                    None
                } else {
                    x as $signed
                }
            }

            fn wrapping_add(self, other: $signed) -> $signed {
                self.wrapping_add(other)
            }

            fn wrapping_sub(self, other: $signed) -> $signed {
                self.wrapping_sub(other)
            }
        }
    };
}

impl_sample!(i8, u8);
impl_sample!(i16, u16);
impl_sample!(i32, u32);
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::{HashMap, BinaryHeap};
use std::borrow::{Cow, IntoCow};

use std::cmp;
use std::path;

use error::StringError;
use constants::*;

#[derive(PartialEq)]
enum CharClass {
    Whitespace,
    Numeric,
    Alphabetic,
    First,
    Other
}

#[derive(Debug)]
struct LineInfo {
    char_map: HashMap<char, Vec<usize>>,
    heatmap: Vec<isize>,
    pub factor: isize
}

#[derive(Debug)]
pub struct LineMatch {
    score: isize,
    factor: isize,
    line: Cow<'static, str>
}

#[derive(Debug)]
pub struct SearchBase {
    lines: HashMap<Cow<'static, str>, LineInfo>
}

impl Default for SearchBase {
    fn default() -> SearchBase {
        SearchBase {
            lines: HashMap::default()
        }
    }
}

impl Ord for LineMatch {
    fn cmp(&self, other: &LineMatch) -> cmp::Ordering {
        match self.score.cmp(&other.score) {
            cmp::Ordering::Equal => self.factor.cmp(&other.factor),
            order => order
        }
    }
}

impl PartialOrd for LineMatch {
    fn partial_cmp(&self, other: &LineMatch) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for LineMatch {
    fn eq(&self, other: &LineMatch) -> bool {
        self.cmp(other) == cmp::Ordering::Equal
    }
}

impl Eq for LineMatch {}

impl SearchBase {
    pub fn read_history<T: AsRef<path::Path>>(&mut self, path: T) -> Result<isize, StringError> {
        let input_file = match File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(e) => return Err(StringError::new("Could not open history file", Some(Box::new(e))))
        };

        let mut line_number = -1;

        for m_line in input_file.lines() {
            let line = match m_line {
                Ok(mut line) => {
                    if line.len() > MAX_LEN {
                        let mut cut_at = None;
                        for (idx, _) in line.char_indices() {
                            if idx > MAX_LEN {
                                cut_at = Some(idx);
                                break;
                            }
                        }
                        match cut_at {
                            None => {
                                // do nothing, last character spans the 80th byte
                            },
                            Some(idx) => {
                                line.truncate(idx);
                            }
                        }
                    }
                    // return the result
                    line
                },
                Err(e) => {
                    return Err(StringError::new("Failed to read line", Some(Box::new(e))));
                }
            };

            line_number += 1;

            // generate the line info
            let info = LineInfo::new(&line, line_number);

            // insert the line into the map
            self.lines.insert(line.into_cow(), info);
        }

        Ok(line_number)
    }

    pub fn query_inplace<T: AsRef<str>>(&self, query: T, matches: &mut BinaryHeap<LineMatch>) {
        // search for a match
        for (line, info) in self.lines.iter() {
            let line_score = match info.query_score(&query) {
                None => {
                    // non-matching line
                    continue;
                },
                Some(score) => {
                    score
                }
            };

            // negate everything so we can use push_pop
            let match_item = LineMatch {
                score: -line_score,
                factor: -info.factor,
                line: line.clone()
            };
            let matches_len = matches.len();
            let matches_capacity = matches.capacity();
            let insert;
            match matches.peek() {
                None => {
                    insert = true;
                },
                Some(item) => {
                    if &match_item < item || matches_len < matches_capacity {
                        insert = true
                    } else {
                        insert = false;
                    }
                }
            }
            if insert {
                if matches_len < matches_capacity {
                    matches.push(match_item);
                } else {
                    matches.push_pop(match_item);
                }
            }
        }
    }

    pub fn query<T: AsRef<str>>(&self, query: T) -> Vec<Cow<'static, str>> {
        // allocate the match object
        let mut matches: BinaryHeap<LineMatch> = BinaryHeap::with_capacity(MATCH_NUMBER);

        self.query_inplace(query, &mut matches);

        // result contains a vector of the top MATCH_NUMBER lines, in descending score order
        matches.into_sorted_vec().into_iter().map(|x| {x.line}).collect()
    }
}

impl LineInfo {
    fn new<T: AsRef<str>>(item: T, factor: isize) -> LineInfo {
        let mut map: HashMap<char, Vec<usize>> = HashMap::new();
        let mut heat = vec![];
        let line = item.as_ref();

        let mut ws_score = 0;
        let mut cs_score = 0;
        let mut cur_class = CharClass::First;
        // character class changes don't stack
        let mut cs_change = false;

        for (idx, c) in line.chars().enumerate() {
            // don't map whitespace
            if !c.is_whitespace() {
                // update the character class change score if needed
                if cur_class == CharClass::First {
                    // add the first character factor on top of class change
                    cs_score += FIRST_FACTOR;
                }
                if c.is_numeric() {
                    if cur_class != CharClass::Numeric {
                        cur_class = CharClass::Numeric;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else if c.is_alphabetic() {
                    if cur_class != CharClass::Alphabetic {
                        cur_class = CharClass::Alphabetic;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else {
                    if cur_class != CharClass::Other {
                        cur_class = CharClass::Other;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                }

                // add an entry in the character map
                map.entry(c).or_insert(Vec::new()).push(idx);
                if c.is_uppercase() {
                    for lc in c.to_lowercase() {
                        // also insert all lowercase equivalents of this character
                        // but not the other way around, so that typing something
                        // uppercase specifies to match uppercase
                        map.entry(lc).or_insert(Vec::new()).push(idx);
                    }
                }
            } else {
                // whitespace is treated differently
                cur_class = CharClass::Whitespace;
                ws_score = WHITESPACE_FACTOR;
            }

            // push to the heatmap
            heat.push(ws_score + cs_score);

            // reduce things
            ws_score /= WHITESPACE_REDUCE;
            if !cs_change {
                cs_score /= CLASS_REDUCE;
            }
        }

        LineInfo {
            char_map: map,
            heatmap: heat,
            factor: factor
        }
    }

    fn query_sequence<T: AsRef<str>>(&self, query_item: T) -> Option<Vec<Vec<usize>>> {
        let query = query_item.as_ref();
        let mut positions: Vec<Vec<usize>> = vec![];

        for c in query.chars() {
            match self.char_map.get(&c) {
                None => break,
                Some(list) => {
                    let to_push;
                    match positions.last() {
                        None => {
                            to_push = list.clone();
                        },
                        Some(item) => {
                            match list.binary_search(&item[0]) {
                                Ok(idx) => {
                                    if idx >= list.len() - 1 {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx + 1).1.into();
                                    }
                                },
                                Err(idx) => {
                                    if idx >= list.len() {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx).1.into();
                                    }
                                }
                            }
                        }
                    }
                    positions.push(to_push);
                }
            }
        }

        if positions.len() == query.len() {
            Some(positions)
        } else {
            None
        }
    }

    fn query_positions<T: AsRef<str>>(&self, query: T) -> Option<Vec<Vec<usize>>> {
        match self.query_sequence(query) {
            None => None,
            Some(positions) => {
                // matching line
                // create our idx vector
                let mut idx = vec![0; positions.len()];
                let mut result = vec![];
                loop {
                    // check that current configuration is strictly increasing
                    let mut ignore = false;
                    {
                        let mut last_pos = None;
                        for (i, pos) in idx.iter().enumerate() {
                            match last_pos {
                                None => last_pos = Some(positions[i][*pos]),
                                Some(other) => {
                                    if other >= positions[i][*pos] {
                                        ignore = true;
                                        break;
                                    } else {
                                        last_pos = Some(positions[i][*pos]);
                                    }
                                }
                            }
                        }
                    }

                    if !ignore {
                        // add the configuration to the list
                        result.push(idx.iter().enumerate().map(|(i, pos)| {positions[i][*pos]}).collect());
                    }

                    // update our position vector
                    let mut update_idx = idx.len() - 1;
                    let mut finished = false;
                    loop {
                        idx[update_idx] += 1;
                        if idx[update_idx] >= positions[update_idx].len() {
                            if update_idx == 0 {
                                // we're finished with all permutations
                                finished = true;
                                break;
                            } else {
                                idx[update_idx] = 0;
                                update_idx -= 1;
                            }
                        } else {
                            // finished updating for this permutation
                            break;
                        }
                    }
                    if finished {
                        // finished with everything
                        break;
                    }
                }

                // return result
                Some(result)
            }
        }
    }

    fn query_score<T: AsRef<str>>(&self, query: T) -> Option<isize> {
        match self.query_positions(query) {
            None => None,
            Some(positions) => {
                let mut top_score = None;
                for pgroup in positions.iter() {
                    // find the average distance between the indexes
                    let mut dist_total = 0;
                    let mut dist_count = 0;
                    for i in 0..pgroup.len() - 1 {
                        dist_total += (pgroup[i + 1] - pgroup[i]) as isize;
                        dist_count += 1;
                    }
                    // avoid division by zero
                    if dist_count == 0 {
                        dist_count = 1;
                    }
                    // sum the heatmap
                    let heat_sum: isize = pgroup.iter().map(|pos| {self.heatmap[*pos]}).sum();
                    let score = (dist_total / dist_count) * DIST_WEIGHT +
                        heat_sum * HEAT_WEIGHT;
                    match top_score {
                        None => top_score = Some(score),
                        Some(last) => {
                            if score > last {
                                top_score = Some(score);
                            }
                        }
                    }
                }

                // return the result
                match top_score {
                    None => None,
                    Some(score) => {
                        Some(score + self.factor / FACTOR_REDUCE)
                    }
                }
            }
        }
    }
}
<commit_msg>Truncate matching differently<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::{HashMap, BinaryHeap};
use std::borrow::{Cow, IntoCow};

use std::cmp;
use std::path;

use error::StringError;
use constants::*;

#[derive(PartialEq)]
enum CharClass {
    Whitespace,
    Numeric,
    Alphabetic,
    First,
    Other
}

#[derive(Debug)]
struct LineInfo {
    char_map: HashMap<char, Vec<usize>>,
    heatmap: Vec<isize>,
    pub factor: isize
}

#[derive(Debug)]
pub struct LineMatch {
    score: isize,
    factor: isize,
    line: Cow<'static, str>
}

#[derive(Debug)]
pub struct SearchBase {
    lines: HashMap<Cow<'static, str>, LineInfo>
}

impl Default for SearchBase {
    fn default() -> SearchBase {
        SearchBase {
            lines: HashMap::default()
        }
    }
}

impl Ord for LineMatch {
    fn cmp(&self, other: &LineMatch) -> cmp::Ordering {
        match self.score.cmp(&other.score) {
            cmp::Ordering::Equal => self.factor.cmp(&other.factor),
            order => order
        }
    }
}

impl PartialOrd for LineMatch {
    fn partial_cmp(&self, other: &LineMatch) -> Option<cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for LineMatch {
    fn eq(&self, other: &LineMatch) -> bool {
        self.cmp(other) == cmp::Ordering::Equal
    }
}

impl Eq for LineMatch {}

impl SearchBase {
    pub fn read_history<T: AsRef<path::Path>>(&mut self, path: T) -> Result<isize, StringError> {
        let input_file = match File::open(path) {
            Ok(f) => BufReader::new(f),
            Err(e) => return Err(StringError::new("Could not open history file", Some(Box::new(e))))
        };

        let mut line_number = -1;

        for m_line in input_file.lines() {
            let line = match m_line {
                Ok(line) => line,
                Err(e) => {
                    return Err(StringError::new("Failed to read line", Some(Box::new(e))));
                }
            };

            line_number += 1;

            // generate the line info
            let info = LineInfo::new(&line, line_number);

            // insert the line into the map
            self.lines.insert(line.into_cow(), info);
        }

        Ok(line_number)
    }

    pub fn query_inplace<T: AsRef<str>>(&self, query: T, matches: &mut BinaryHeap<LineMatch>) {
        // search for a match
        for (line, info) in self.lines.iter() {
            let line_score = match info.query_score(&query) {
                None => {
                    // non-matching line
                    continue;
                },
                Some(score) => {
                    score
                }
            };

            // negate everything so we can use push_pop
            let match_item = LineMatch {
                score: -line_score,
                factor: -info.factor,
                line: line.clone()
            };
            let matches_len = matches.len();
            let matches_capacity = matches.capacity();
            let insert;
            match matches.peek() {
                None => {
                    insert = true;
                },
                Some(item) => {
                    if &match_item < item || matches_len < matches_capacity {
                        insert = true
                    } else {
                        insert = false;
                    }
                }
            }
            if insert {
                if matches_len < matches_capacity {
                    matches.push(match_item);
                } else {
                    matches.push_pop(match_item);
                }
            }
        }
    }

    pub fn query<T: AsRef<str>>(&self, query: T) -> Vec<Cow<'static, str>> {
        // allocate the match object
        let mut matches: BinaryHeap<LineMatch> = BinaryHeap::with_capacity(MATCH_NUMBER);

        self.query_inplace(query, &mut matches);

        // result contains a vector of the top MATCH_NUMBER lines, in descending score order
        matches.into_sorted_vec().into_iter().map(|x| {x.line}).collect()
    }
}

impl LineInfo {
    fn new<T: AsRef<str>>(item: T, factor: isize) -> LineInfo {
        let mut map: HashMap<char, Vec<usize>> = HashMap::new();
        let mut heat = vec![];
        let line = item.as_ref();

        let mut ws_score = 0;
        let mut cs_score = 0;
        let mut cur_class = CharClass::First;
        // character class changes don't stack
        let mut cs_change = false;

        for (idx, c) in line.chars().enumerate() {
            // don't process characters beyond MAX_LEN
            if idx > MAX_LEN {
                break;
            }

            // don't map whitespace
            if !c.is_whitespace() {
                // update the character class change score if needed
                if cur_class == CharClass::First {
                    // add the first character factor on top of class change
                    cs_score += FIRST_FACTOR;
                }
                if c.is_numeric() {
                    if cur_class != CharClass::Numeric {
                        cur_class = CharClass::Numeric;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else if c.is_alphabetic() {
                    if cur_class != CharClass::Alphabetic {
                        cur_class = CharClass::Alphabetic;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else {
                    if cur_class != CharClass::Other {
                        cur_class = CharClass::Other;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                }

                // add an entry in the character map
                map.entry(c).or_insert(Vec::new()).push(idx);
                if c.is_uppercase() {
                    for lc in c.to_lowercase() {
                        // also insert all lowercase equivalents of this character
                        // but not the other way around, so that typing something
                        // uppercase specifies to match uppercase
                        map.entry(lc).or_insert(Vec::new()).push(idx);
                    }
                }
            } else {
                // whitespace is treated differently
                cur_class = CharClass::Whitespace;
                ws_score = WHITESPACE_FACTOR;
            }

            // push to the heatmap
            heat.push(ws_score + cs_score);

            // reduce things
            ws_score /= WHITESPACE_REDUCE;
            if !cs_change {
                cs_score /= CLASS_REDUCE;
            }
        }

        LineInfo {
            char_map: map,
            heatmap: heat,
            factor: factor
        }
    }

    fn query_sequence<T: AsRef<str>>(&self, query_item: T) -> Option<Vec<Vec<usize>>> {
        let query = query_item.as_ref();
        let mut positions: Vec<Vec<usize>> = vec![];

        for c in query.chars() {
            match self.char_map.get(&c) {
                None => break,
                Some(list) => {
                    let to_push;
                    match positions.last() {
                        None => {
                            to_push = list.clone();
                        },
                        Some(item) => {
                            match list.binary_search(&item[0]) {
                                Ok(idx) => {
                                    if idx >= list.len() - 1 {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx + 1).1.into();
                                    }
                                },
                                Err(idx) => {
                                    if idx >= list.len() {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx).1.into();
                                    }
                                }
                            }
                        }
                    }
                    positions.push(to_push);
                }
            }
        }

        if positions.len() == query.len() {
            Some(positions)
        } else {
            None
        }
    }

    fn query_positions<T: AsRef<str>>(&self, query: T) -> Option<Vec<Vec<usize>>> {
        match self.query_sequence(query) {
            None => None,
            Some(positions) => {
                // matching line
                // create our idx vector
                let mut idx = vec![0; positions.len()];
                let mut result = vec![];
                loop {
                    // check that current configuration is strictly increasing
                    let mut ignore = false;
                    {
                        let mut last_pos = None;
                        for (i, pos) in idx.iter().enumerate() {
                            match last_pos {
                                None => last_pos = Some(positions[i][*pos]),
                                Some(other) => {
                                    if other >= positions[i][*pos] {
                                        ignore = true;
                                        break;
                                    } else {
                                        last_pos = Some(positions[i][*pos]);
                                    }
                                }
                            }
                        }
                    }

                    if !ignore {
                        // add the configuration to the list
                        result.push(idx.iter().enumerate().map(|(i, pos)| {positions[i][*pos]}).collect());
                    }

                    // update our position vector
                    let mut update_idx = idx.len() - 1;
                    let mut finished = false;
                    loop {
                        idx[update_idx] += 1;
                        if idx[update_idx] >= positions[update_idx].len() {
                            if update_idx == 0 {
                                // we're finished with all permutations
                                finished = true;
                                break;
                            } else {
                                idx[update_idx] = 0;
                                update_idx -= 1;
                            }
                        } else {
                            // finished updating for this permutation
                            break;
                        }
                    }
                    if finished {
                        // finished with everything
                        break;
                    }
                }

                // return result
                Some(result)
            }
        }
    }

    fn query_score<T: AsRef<str>>(&self, query: T) -> Option<isize> {
        match self.query_positions(query) {
            None => None,
            Some(positions) => {
                let mut top_score = None;
                for pgroup in positions.iter() {
                    // find the average distance between the indexes
                    let mut dist_total = 0;
                    let mut dist_count = 0;
                    for i in 0..pgroup.len() - 1 {
                        dist_total += (pgroup[i + 1] - pgroup[i]) as isize;
                        dist_count += 1;
                    }
                    // avoid division by zero
                    if dist_count == 0 {
                        dist_count = 1;
                    }
                    // sum the heatmap
                    let heat_sum: isize = pgroup.iter().map(|pos| {self.heatmap[*pos]}).sum();
                    let score = (dist_total / dist_count) * DIST_WEIGHT +
                        heat_sum * HEAT_WEIGHT;
                    match top_score {
                        None => top_score = Some(score),
                        Some(last) => {
                            if score > last {
                                top_score = Some(score);
                            }
                        }
                    }
                }

                // return the result
                match top_score {
                    None => None,
                    Some(score) => {
                        Some(score + self.factor / FACTOR_REDUCE)
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::hash::{Hash, Hasher, SipHasher};
use std::collections::HashSet;

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=Self::Action>;

    fn actions(&self, state: &Self::State) -> Self::Iterator;
    fn next_state(&self, state: &Self::State, action: &Self::Action) -> Self::State;
    fn is_goal(&self, state: &Self::State) -> bool;
}

pub fn depth_first_search<S>(space: &S, start: S::State) -> Option<Vec<S::Action>>
where S: SearchSpace {
    let mut path = Vec::new();
    let actions = space.actions(&start);
    let mut frontier = vec![(start, actions)];
    let mut visited = HashSet::new();

    loop {
        let result = match frontier.last_mut() {
            None => return None,
            Some(&mut (ref mut state, ref mut actions)) => {
                if space.is_goal(&state) {
                    return Some(path);
                }
                match actions.next() {
                    None => None,
                    Some(action) => {
                        let next_state = space.next_state(&state, &action);

                        let mut hasher = SipHasher::new();
                        next_state.hash(&mut hasher);
                        let hash = hasher.finish();
                        if visited.contains(&hash) {
                            continue;
                        }
                        visited.insert(hash);

                        let next_actions = space.actions(&next_state);
                        path.push(action);
                        Some((next_state, next_actions))
                    }
                }
            }
        };
        match result {
            None => {
                path.pop();
                frontier.pop();
            },
            Some((next_state, next_actions)) => {
                frontier.push((next_state, next_actions));
            },
        };
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::*;

    #[test]
    pub fn test_depth_first_search() {
        struct S {
            goal: i32,
        }

        #[derive(Debug, Clone, PartialEq)]
        enum Direction {
            Left,
            Right
        }

        impl SearchSpace for S {
            type State = i32;
            type Action = Direction;
            type Iterator = IntoIter<Self::Action>;

            fn actions(&self, state: &i32) -> Self::Iterator {
                if *state == 0 || *state == 1 {
                    vec![Direction::Left, Direction::Right].into_iter()
                } else if *state == 2 {
                    vec![Direction::Left].into_iter()
                }
                else {
                    vec![].into_iter()
                }
            }

            fn next_state(&self, state: &i32, action: &Direction) -> i32 {
                match *state {
                    0 => match *action {
                        Direction::Left => 1,
                        Direction::Right => 2,
                    },
                    1 => match *action {
                        Direction::Left => 3,
                        Direction::Right => 4,
                    },
                    2 => 2,
                    node => node
                }
            }

            fn is_goal(&self, state: &i32) -> bool {
                return self.goal == *state
            }
        }

        impl S {
            fn solve(start: i32, goal: i32) -> Option<Vec<Direction>> {
                let space = S { goal: goal };
                depth_first_search(&space, start)
            }
        }

        assert_eq!(S::solve(0, 0).unwrap(), vec![]);
        assert_eq!(S::solve(0, 1).unwrap(), vec![Direction::Left]);
        assert_eq!(S::solve(0, 2).unwrap(), vec![Direction::Right]);
        assert_eq!(S::solve(0, 3).unwrap(), vec![Direction::Left, Direction::Left]);
        assert_eq!(S::solve(0, 4).unwrap(), vec![Direction::Left, Direction::Right]);
        assert_eq!(S::solve(2, 2).unwrap(), vec![]);
        assert!(S::solve(2, 0).is_none());
        assert!(S::solve(5, 0).is_none());
    }
}
<commit_msg>Import std::iter::Iterator explicitly<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::hash::{Hash, Hasher, SipHasher};

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=Self::Action>;

    fn actions(&self, state: &Self::State) -> Self::Iterator;
    fn next_state(&self, state: &Self::State, action: &Self::Action) -> Self::State;
    fn is_goal(&self, state: &Self::State) -> bool;
}

pub fn depth_first_search<S>(space: &S, start: S::State) -> Option<Vec<S::Action>>
where S: SearchSpace {
    let mut path = Vec::new();
    let actions = space.actions(&start);
    let mut frontier = vec![(start, actions)];
    let mut visited = HashSet::new();

    loop {
        let result = match frontier.last_mut() {
            None => return None,
            Some(&mut (ref mut state, ref mut actions)) => {
                if space.is_goal(&state) {
                    return Some(path);
                }
                match actions.next() {
                    None => None,
                    Some(action) => {
                        let next_state = space.next_state(&state, &action);

                        let mut hasher = SipHasher::new();
                        next_state.hash(&mut hasher);
                        let hash = hasher.finish();
                        if visited.contains(&hash) {
                            continue;
                        }
                        visited.insert(hash);

                        let next_actions = space.actions(&next_state);
                        path.push(action);
                        Some((next_state, next_actions))
                    }
                }
            }
        };
        match result {
            None => {
                path.pop();
                frontier.pop();
            },
            Some((next_state, next_actions)) => {
                frontier.push((next_state, next_actions));
            },
        };
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::*;

    #[test]
    pub fn test_depth_first_search() {
        struct S {
            goal: i32,
        }

        #[derive(Debug, Clone, PartialEq)]
        enum Direction {
            Left,
            Right
        }

        impl SearchSpace for S {
            type State = i32;
            type Action = Direction;
            type Iterator = IntoIter<Self::Action>;

            fn actions(&self, state: &i32) -> Self::Iterator {
                if *state == 0 || *state == 1 {
                    vec![Direction::Left, Direction::Right].into_iter()
                } else if *state == 2 {
                    vec![Direction::Left].into_iter()
                }
                else {
                    vec![].into_iter()
                }
            }

            fn next_state(&self, state: &i32, action: &Direction) -> i32 {
                match *state {
                    0 => match *action {
                        Direction::Left => 1,
                        Direction::Right => 2,
                    },
                    1 => match *action {
                        Direction::Left => 3,
                        Direction::Right => 4,
                    },
                    2 => 2,
                    node => node
                }
            }

            fn is_goal(&self, state: &i32) -> bool {
                return self.goal == *state
            }
        }

        impl S {
            fn solve(start: i32, goal: i32) -> Option<Vec<Direction>> {
                let space = S { goal: goal };
                depth_first_search(&space, start)
            }
        }

        assert_eq!(S::solve(0, 0).unwrap(), vec![]);
        assert_eq!(S::solve(0, 1).unwrap(), vec![Direction::Left]);
        assert_eq!(S::solve(0, 2).unwrap(), vec![Direction::Right]);
        assert_eq!(S::solve(0, 3).unwrap(), vec![Direction::Left, Direction::Left]);
        assert_eq!(S::solve(0, 4).unwrap(), vec![Direction::Left, Direction::Right]);
        assert_eq!(S::solve(2, 2).unwrap(), vec![]);
        assert!(S::solve(2, 0).is_none());
        assert!(S::solve(5, 0).is_none());
    }
}
<|endoftext|>


<commit_before>//! Various helpers for Actix applications to use during testing.
use std::sync::mpsc;
use std::{net, thread, time};

use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{Runtime, System};
use actix_server::{Server, StreamServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
use bytes::Bytes;
use futures::future::lazy;
use futures::{Future, Stream};
use http::Method;
use net2::TcpBuilder;

/// The `TestServer` type.
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests cases for actix web applications.
///
/// # Examples
///
/// ```rust
/// use actix_http::HttpService;
/// use actix_http_test::TestServer;
/// use actix_web::{web, App, HttpResponse};
///
/// fn my_handler() -> HttpResponse {
///     HttpResponse::Ok().into()
/// }
///
/// fn main() {
///     let mut srv = TestServer::new(
///         || HttpService::new(
///             App::new().service(
///                 web::resource("/").to(my_handler))
///         )
///     );
///
///     let req = srv.get();
///     let response = srv.block_on(req.send()).unwrap();
///     assert!(response.status().is_success());
/// }
/// ```
pub struct TestServer;

/// Test server controller
pub struct TestServerRuntime {
    addr: net::SocketAddr,
    rt: Runtime,
    client: Client,
}

impl TestServer {
    /// Start new test server with application factory
    pub fn new<F: StreamServiceFactory>(factory: F) -> TestServerRuntime {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        thread::spawn(move || {
            let sys = System::new("actix-test-server");
            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();

            Server::build()
                .listen("test", tcp, factory)?
                .workers(1)
                .disable_signals()
                .start();

            tx.send((System::current(), local_addr)).unwrap();
            sys.run()
        });

        let (system, addr) = rx.recv().unwrap();
        let mut rt = Runtime::new().unwrap();

        let client = rt
            .block_on(lazy(move || {
                let connector = {
                    #[cfg(feature = "ssl")]
                    {
                        use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};

                        let mut builder =
                            SslConnector::builder(SslMethod::tls()).unwrap();
                        builder.set_verify(SslVerifyMode::NONE);
                        let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").map_err(
                            |e| log::error!("Can not set alpn protocol: {:?}", e),
                        );
                        Connector::new()
                            .timeout(time::Duration::from_millis(500))
                            .ssl(builder.build())
                            .service()
                    }
                    #[cfg(not(feature = "ssl"))]
                    {
                        Connector::new()
                            .timeout(time::Duration::from_millis(500))
                            .service()
                    }
                };

                Ok::<Client, ()>(Client::build().connector(connector).finish())
            }))
            .unwrap();
        System::set_current(system);
        TestServerRuntime { addr, rt, client }
    }

    /// Get firat available unused address
    pub fn unused_addr() -> net::SocketAddr {
        let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
        let socket = TcpBuilder::new_v4().unwrap();
        socket.bind(&addr).unwrap();
        socket.reuse_address(true).unwrap();
        let tcp = socket.to_tcp_listener().unwrap();
        tcp.local_addr().unwrap()
    }
}

impl TestServerRuntime {
    /// Execute future on current core
    pub fn block_on<F, I, E>(&mut self, fut: F) -> Result<I, E>
    where
        F: Future<Item = I, Error = E>,
    {
        self.rt.block_on(fut)
    }

    /// Execute function on current core
    pub fn execute<F, R>(&mut self, fut: F) -> R
    where
        F: FnOnce() -> R,
    {
        self.rt.block_on(lazy(|| Ok::<_, ()>(fut()))).unwrap()
    }

    /// Construct test server url
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }

    /// Construct test server url
    pub fn url(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("http://127.0.0.1:{}{}", self.addr.port(), uri)
        } else {
            format!("http://127.0.0.1:{}/{}", self.addr.port(), uri)
        }
    }

    /// Construct test https server url
    pub fn surl(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("https://127.0.0.1:{}{}", self.addr.port(), uri)
        } else {
            format!("https://127.0.0.1:{}/{}", self.addr.port(), uri)
        }
    }

    /// Create `GET` request
    pub fn get(&self) -> ClientRequest {
        self.client.get(self.url("/").as_str())
    }

    /// Create https `GET` request
    pub fn sget(&self) -> ClientRequest {
        self.client.get(self.surl("/").as_str())
    }

    /// Create `POST` request
    pub fn post(&self) -> ClientRequest {
        self.client.post(self.url("/").as_str())
    }

    /// Create https `POST` request
    pub fn spost(&self) -> ClientRequest {
        self.client.post(self.surl("/").as_str())
    }

    /// Create `HEAD` request
    pub fn head(&self) -> ClientRequest {
        self.client.head(self.url("/").as_str())
    }

    /// Create https `HEAD` request
    pub fn shead(&self) -> ClientRequest {
        self.client.head(self.surl("/").as_str())
    }

    /// Connect to test http server
    pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
        self.client.request(method, path.as_ref())
    }

    pub fn load_body<S>(
        &mut self,
        response: ClientResponse<S>,
    ) -> Result<Bytes, PayloadError>
    where
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        self.block_on(response.body().limit(10_485_760))
    }

    /// Connect to websocket server at a given path
    pub fn ws_at(
        &mut self,
        path: &str,
    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
    {
        let url = self.url(path);
        let connect = self.client.ws(url).connect();
        self.rt
            .block_on(lazy(move || connect.map(|(_, framed)| framed)))
    }

    /// Connect to a websocket server
    pub fn ws(
        &mut self,
    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
    {
        self.ws_at("/")
    }

    /// Stop http server
    fn stop(&mut self) {
        System::current().stop();
    }
}

impl Drop for TestServerRuntime {
    fn drop(&mut self) {
        self.stop()
    }
}
<commit_msg>test-server: Request functions should accept path (#743)<commit_after>//! Various helpers for Actix applications to use during testing.
use std::sync::mpsc;
use std::{net, thread, time};

use actix_codec::{AsyncRead, AsyncWrite, Framed};
use actix_rt::{Runtime, System};
use actix_server::{Server, StreamServiceFactory};
use awc::{error::PayloadError, ws, Client, ClientRequest, ClientResponse, Connector};
use bytes::Bytes;
use futures::future::lazy;
use futures::{Future, Stream};
use http::Method;
use net2::TcpBuilder;

/// The `TestServer` type.
///
/// `TestServer` is very simple test server that simplify process of writing
/// integration tests cases for actix web applications.
///
/// # Examples
///
/// ```rust
/// use actix_http::HttpService;
/// use actix_http_test::TestServer;
/// use actix_web::{web, App, HttpResponse};
///
/// fn my_handler() -> HttpResponse {
///     HttpResponse::Ok().into()
/// }
///
/// fn main() {
///     let mut srv = TestServer::new(
///         || HttpService::new(
///             App::new().service(
///                 web::resource("/").to(my_handler))
///         )
///     );
///
///     let req = srv.get();
///     let response = srv.block_on(req.send()).unwrap();
///     assert!(response.status().is_success());
/// }
/// ```
pub struct TestServer;

/// Test server controller
pub struct TestServerRuntime {
    addr: net::SocketAddr,
    rt: Runtime,
    client: Client,
}

impl TestServer {
    /// Start new test server with application factory
    pub fn new<F: StreamServiceFactory>(factory: F) -> TestServerRuntime {
        let (tx, rx) = mpsc::channel();

        // run server in separate thread
        thread::spawn(move || {
            let sys = System::new("actix-test-server");
            let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
            let local_addr = tcp.local_addr().unwrap();

            Server::build()
                .listen("test", tcp, factory)?
                .workers(1)
                .disable_signals()
                .start();

            tx.send((System::current(), local_addr)).unwrap();
            sys.run()
        });

        let (system, addr) = rx.recv().unwrap();
        let mut rt = Runtime::new().unwrap();

        let client = rt
            .block_on(lazy(move || {
                let connector = {
                    #[cfg(feature = "ssl")]
                    {
                        use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};

                        let mut builder =
                            SslConnector::builder(SslMethod::tls()).unwrap();
                        builder.set_verify(SslVerifyMode::NONE);
                        let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").map_err(
                            |e| log::error!("Can not set alpn protocol: {:?}", e),
                        );
                        Connector::new()
                            .timeout(time::Duration::from_millis(500))
                            .ssl(builder.build())
                            .service()
                    }
                    #[cfg(not(feature = "ssl"))]
                    {
                        Connector::new()
                            .timeout(time::Duration::from_millis(500))
                            .service()
                    }
                };

                Ok::<Client, ()>(Client::build().connector(connector).finish())
            }))
            .unwrap();
        System::set_current(system);
        TestServerRuntime { addr, rt, client }
    }

    /// Get firat available unused address
    pub fn unused_addr() -> net::SocketAddr {
        let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
        let socket = TcpBuilder::new_v4().unwrap();
        socket.bind(&addr).unwrap();
        socket.reuse_address(true).unwrap();
        let tcp = socket.to_tcp_listener().unwrap();
        tcp.local_addr().unwrap()
    }
}

impl TestServerRuntime {
    /// Execute future on current core
    pub fn block_on<F, I, E>(&mut self, fut: F) -> Result<I, E>
    where
        F: Future<Item = I, Error = E>,
    {
        self.rt.block_on(fut)
    }

    /// Execute function on current core
    pub fn execute<F, R>(&mut self, fut: F) -> R
    where
        F: FnOnce() -> R,
    {
        self.rt.block_on(lazy(|| Ok::<_, ()>(fut()))).unwrap()
    }

    /// Construct test server url
    pub fn addr(&self) -> net::SocketAddr {
        self.addr
    }

    /// Construct test server url
    pub fn url(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("http://127.0.0.1:{}{}", self.addr.port(), uri)
        } else {
            format!("http://127.0.0.1:{}/{}", self.addr.port(), uri)
        }
    }

    /// Construct test https server url
    pub fn surl(&self, uri: &str) -> String {
        if uri.starts_with('/') {
            format!("https://127.0.0.1:{}{}", self.addr.port(), uri)
        } else {
            format!("https://127.0.0.1:{}/{}", self.addr.port(), uri)
        }
    }

    /// Create `GET` request
    pub fn get<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.get(self.url(path.as_ref()).as_str())
    }

    /// Create https `GET` request
    pub fn sget<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.get(self.surl(path.as_ref()).as_str())
    }

    /// Create `POST` request
    pub fn post<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.post(self.url(path.as_ref()).as_str())
    }

    /// Create https `POST` request
    pub fn spost<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.post(self.surl(path.as_ref()).as_str())
    }

    /// Create `HEAD` request
    pub fn head<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.head(self.url(path.as_ref()).as_str())
    }

    /// Create https `HEAD` request
    pub fn shead<S: AsRef<str>>(&self, path: S) -> ClientRequest {
        self.client.head(self.surl(path.as_ref()).as_str())
    }

    /// Connect to test http server
    pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
        self.client.request(method, path.as_ref())
    }

    pub fn load_body<S>(
        &mut self,
        response: ClientResponse<S>,
    ) -> Result<Bytes, PayloadError>
    where
        S: Stream<Item = Bytes, Error = PayloadError> + 'static,
    {
        self.block_on(response.body().limit(10_485_760))
    }

    /// Connect to websocket server at a given path
    pub fn ws_at(
        &mut self,
        path: &str,
    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
    {
        let url = self.url(path);
        let connect = self.client.ws(url).connect();
        self.rt
            .block_on(lazy(move || connect.map(|(_, framed)| framed)))
    }

    /// Connect to a websocket server
    pub fn ws(
        &mut self,
    ) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError>
    {
        self.ws_at("/")
    }

    /// Stop http server
    fn stop(&mut self) {
        System::current().stop();
    }
}

impl Drop for TestServerRuntime {
    fn drop(&mut self) {
        self.stop()
    }
}
<|endoftext|>


<commit_before><commit_msg>Factor out some copy and paste in the line number program benches<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::net::{Ipv4Addr, SocketAddrV4};
use std::sync::Arc;

use slog::{info, o, Logger};
use tokio::io::Result;
use tokio::net::UdpSocket;

use crate::config::{Config, ConnectionConfig};

/// Server is the UDP server main implementation
pub struct Server {
    log: Logger,
}

impl Server {
    pub fn new(base: Logger) -> Self {
        let log = base.new(o!("source" => "server::Server"));
        return Server { log };
    }

    /// start the async processing of UDP packets
    pub async fn run(self, config: Arc<Config>) -> Result<()> {
        let Server { log } = self;
        info!(log, "Starting on port {}", config.local.port);

        // TOXO: work from here. Probably need a loop at this point?
        let _socket = Server::bind(&config).await?;

        match &config.connections {
            ConnectionConfig::Sender { address, .. } => {
                info!(log, "Sender configuration"; "address" => address)
            }
            ConnectionConfig::Receiver { endpoints } => {
                info!(log, "Receiver configuration"; "endpoints" => endpoints.len())
            }
        };

        return Ok(());
    }

    /// bind binds the local configured port
    async fn bind(config: &Config) -> Result<UdpSocket> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), config.local.port);
        return UdpSocket::bind(addr).await;
    }
}

#[cfg(test)]
mod tests {
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};

    use crate::config::{Config, ConnectionConfig, Local};
    use crate::server::Server;

    #[tokio::test]
    async fn bind() {
        let config = Config {
            local: Local { port: 12345 },
            connections: ConnectionConfig::Receiver {
                endpoints: Vec::new(),
            },
        };
        let socket = Server::bind(&config).await.unwrap();
        let addr = socket.local_addr().unwrap();

        let expected = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12345);
        assert_eq!(expected, addr)
    }
}
<commit_msg>WE ARE RECIEVING A PACKET!!!<commit_after>/*
 * Copyright 2020 Google LLC All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::str::from_utf8;
use std::sync::Arc;

use slog::{debug, info, o, Logger};
use tokio::io::Result;
use tokio::net::UdpSocket;

use crate::config::{Config, ConnectionConfig};

/// Server is the UDP server main implementation
pub struct Server {
    log: Logger,
}

impl Server {
    pub fn new(base: Logger) -> Self {
        let log = base.new(o!("source" => "server::Server"));
        return Server { log };
    }

    /// start the async processing of UDP packets
    pub async fn run(self, config: Arc<Config>) -> Result<()> {
        info!(self.log, "Starting on port {}", config.local.port);
        match &config.connections {
            ConnectionConfig::Sender { address, .. } => {
                info!(self.log, "Sender configuration"; "address" => address)
            }
            ConnectionConfig::Receiver { endpoints } => {
                info!(self.log, "Receiver configuration"; "endpoints" => endpoints.len())
            }
        };

        let mut socket = Server::bind(&config).await?;
        let mut buf: Vec<u8> = vec![0; 65535];
        loop {
            debug!(self.log, "awaiting packet");
            let (size, addr) = socket.recv_from(&mut buf).await?;
            // TOXO: make this async
            self.receive_packet(&buf[..size], addr);
        }
    }

    /// bind binds the local configured port
    async fn bind(config: &Config) -> Result<UdpSocket> {
        let addr = SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), config.local.port);
        return UdpSocket::bind(addr).await;
    }

    /// receive_packet provides the logic for what to do when a packet comes in!
    fn receive_packet(&self, buf: &[u8], addr: SocketAddr) {
        let s = from_utf8(buf).unwrap();
        info!(self.log, "Packet Received from {}: {}", addr, s);
    }
}

#[cfg(test)]
mod tests {
    use std::net::{IpAddr, Ipv4Addr, SocketAddr};

    use crate::config::{Config, ConnectionConfig, Local};
    use crate::server::Server;

    #[tokio::test]
    async fn bind() {
        let config = Config {
            local: Local { port: 12345 },
            connections: ConnectionConfig::Receiver {
                endpoints: Vec::new(),
            },
        };
        let socket = Server::bind(&config).await.unwrap();
        let addr = socket.local_addr().unwrap();

        let expected = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12345);
        assert_eq!(expected, addr)
    }
}
<|endoftext|>


<commit_before>use luminance::{FragmentShader, GeometryShader, StageError, TessellationControlShader,
                TessellationEvaluationShader, VertexShader};
use luminance_gl::gl33::{ProgramProxy, Stage};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;

pub use luminance::{ProgramError, Uniformable, UniformUpdate};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::{self, Uniform};
pub use luminance_gl::gl33::token::*;

use resource::{Cache, Load, LoadError, Reload};

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

/// Build a shader program from the shader sources and a uniform builder function.
pub fn new_program<T>(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str, get_uni: &'static Fn(ProgramProxy) -> Result<T, UniformWarning>) -> Result<gl33::Program<T>, ProgramError> {
  let stages = compile_stages(tcs_src, tes_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, get_uni)
        } else {
          gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, get_uni)
        }
      } else if let Some(gs) = gs {
        gl33::Program::new(None, &vs, Some(&gs), &fs, get_uni)
      } else {
        gl33::Program::new(None, &vs, None, &fs, get_uni)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

// Take raw shader sources and turn them into stages.
fn compile_stages(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str) -> Result<(Option<(Stage<TessellationControlShader>, Stage<TessellationEvaluationShader>)>, Stage<VertexShader>, Option<Stage<GeometryShader>>, Stage<FragmentShader>), StageError> {
  let tess = if !tcs_src.is_empty() && !tes_src.is_empty() { Some((try!(Stage::new(tcs_src)), try!(Stage::new(tes_src)))) } else { None };
  let vs = try!(Stage::new(vs_src));
  let gs = if !gs_src.is_empty() { Some(try!(Stage::new(gs_src))) } else { None };
  let fs = try!(Stage::new(fs_src));

  Ok((tess, vs, gs, fs))
}

/// Shader program.
///
/// If the program is retrieved from the cache, the path must point to a file containing all the
/// stages. A stage begins with a stage pragma indicating which kind of stage the following source
/// is. Here’s a list of all pragma for each kind of stage:
///
/// - `#tcs`: *tessellation control stage*
/// - `#tes`: *tessellation evaluation stage*
/// - `#vs`: *vertex stage*
/// - `#gs`: *geometry stage*
/// - `#fs`: *fragment stage*
///
/// A stage starts at such a pragma listed above and ends at the next, different pragma. You cannot
/// use twice the same pragma in a file.
///
/// At the top of the file, if you don’t put a pragma, you can use `//` to add comments, or die.
pub struct Program<T> where T: 'static {
  pub program: gl33::Program<T>,
  get_uni: &'static Fn(ProgramProxy) -> Result<T, UniformWarning>,
}

impl<T> Deref for Program<T> where T: 'static {
  type Target = gl33::Program<T>;

  fn deref(&self) -> &Self::Target {
    &self.program
  }
}

impl<T> Load for Program<T> where T: 'static {
  type Args = &'static Fn(ProgramProxy) -> Result<T, UniformWarning>;

  fn load<'a, P>(path: P, _: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path> {
    enum CurrentStage {
      VS,
      FS,
      GS,
      TCS,
      TES
    }

    fn add_line_to_src(src: &mut String, line: &String, line_nb: usize) {
      if src.is_empty() {
        *src += &format!("#line {}\n{}\n", line_nb, line);
      }
    }

    match File::open(path.as_ref()) {
      Ok(fh) => {
        let buffered = BufReader::new(fh);
        let mut tcs_src = String::new();
        let mut tes_src = String::new();
        let mut vs_src = String::new();
        let mut gs_src = String::new();
        let mut fs_src = String::new();
        let mut current_stage: Option<CurrentStage> = None;

        for (line_nb, line) in buffered.lines().enumerate() {
          let line_nb = line_nb + 1;
          let line = line.unwrap();
          let trimmed = line.trim();

          if trimmed.starts_with("#vs") {
            if !vs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::VS);
          } else if trimmed.starts_with("#fs") {
            if !fs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::FS);
          } else if trimmed.starts_with("#gs") {
            if !gs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::GS);
          } else if trimmed.starts_with("#tcs") {
            if !tcs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::TCS);
          } else if trimmed.starts_with("#tes") {
            if !tes_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
            }

            current_stage = Some(CurrentStage::TES);
          } else if current_stage.is_none() && !trimmed.starts_with("//") {
            return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
          }

          match current_stage {
            Some(CurrentStage::VS) => {
              add_line_to_src(&mut vs_src, &line, line_nb);
            },
            Some(CurrentStage::FS) => {
              add_line_to_src(&mut fs_src, &line, line_nb);
            },
            Some(CurrentStage::GS) => {
              add_line_to_src(&mut gs_src, &line, line_nb);
            },
            Some(CurrentStage::TCS) => {
              add_line_to_src(&mut tcs_src, &line, line_nb);
            },
            Some(CurrentStage::TES) => {
              add_line_to_src(&mut tes_src, &line, line_nb);
            },
            None => {}
          }
        }

        let program = try!(new_program(&tcs_src, &tes_src, &vs_src, &gs_src, &fs_src, args)
          .map_err(|e| LoadError::ConversionFailed(format!("{:?}", e))));

        Ok(Program {
          program: program,
          get_uni: args
        })
      },
      Err(e) => {
        Err(LoadError::FileNotFound(path.as_ref().to_owned(), format!("{:?}", e)))
      }
    }
  }
}

impl<T> Reload for Program<T> where T: 'static {
  fn reload_args(&self) -> Self::Args {
    self.get_uni
  }
}

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
pub fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable<GL33> {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<commit_msg>Fixed shaders regarding the new luminance’s ones.<commit_after>use luminance::{Sem, StageError};
use luminance::shader::stage;
use luminance_gl::gl33::Stage;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::ops::Deref;
use std::path::Path;

pub use luminance::{ProgramError,Uniformable};
pub use luminance::shader::program::UniformWarning;
pub use luminance_gl::gl33::{self, Uniform};
pub use luminance_gl::gl33::token::GL33;

use resource::{Cache, Load, LoadError, Reload};

#[derive(Debug)]
pub enum ShaderError {
  StageError(StageError),
  ProgramError(ProgramError)
}

pub fn new_program(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str, sem_map: &[Sem]) -> Result<(gl33::Program, Vec<UniformWarning>), ProgramError> {
  let stages = compile_stages(tcs_src, tes_src, vs_src, gs_src, fs_src);

  match stages {
    Ok((tess, vs, gs, fs)) => {
      if let Some((tcs, tes)) = tess {
        if let Some(gs) = gs {
          gl33::Program::new(Some((&tcs, &tes)), &vs, Some(&gs), &fs, sem_map)
        } else {
          gl33::Program::new(Some((&tcs, &tes)), &vs, None, &fs, sem_map)
        }
      } else if let Some(gs) = gs {
        gl33::Program::new(None, &vs, Some(&gs), &fs, sem_map)
      } else {
        gl33::Program::new(None, &vs, None, &fs, sem_map)
      }
    },
    Err(stage_error) => {
      Err(ProgramError::LinkFailed(format!("{:?}", stage_error)))
    }
  }
}

// Take raw shader sources and turn them into stages.
fn compile_stages(tcs_src: &str, tes_src: &str, vs_src: &str, gs_src: &str, fs_src: &str) -> Result<(Option<(Stage, Stage)>, Stage, Option<Stage>, Stage), StageError> {
  let tess = if !tcs_src.is_empty() && !tes_src.is_empty() {
    Some((try!(Stage::new(stage::Type::TessellationControlShader, tcs_src)),
          try!(Stage::new(stage::Type::TessellationEvaluationShader, tes_src))))
  } else {
    None
  };

  let vs = try!(Stage::new(stage::Type::VertexShader, vs_src));
  let gs = if !gs_src.is_empty() { Some(try!(Stage::new(stage::Type::GeometryShader, gs_src))) } else { None };
  let fs = try!(Stage::new(stage::Type::FragmentShader, fs_src));

  Ok((tess, vs, gs, fs))
}

/// Shader program.
///
/// If the program is retrieved from the cache, the path must point to a file containing all the
/// stages. A stage begins with a stage pragma indicating which kind of stage the following source
/// is. Here’s a list of all pragma for each kind of stage:
///
/// - `#tcs`: *tessellation control stage*
/// - `#tes`: *tessellation evaluation stage*
/// - `#vs`: *vertex stage*
/// - `#gs`: *geometry stage*
/// - `#fs`: *fragment stage*
///
/// A stage starts at such a pragma listed above and ends at the next, different pragma. You cannot
/// use twice the same pragma in a file.
///
/// At the top of the file, if you don’t put a pragma, you can use `//` to add comments, or die.
pub struct Program {
  program: gl33::Program,
  sem_map: Vec<Sem>
}

impl Load for Program {
  type Args = Vec<Sem>;

  fn load<'a, P>(path: P, _: &mut Cache<'a>, args: Self::Args) -> Result<Self, LoadError> where P: AsRef<Path> {
    enum CurrentStage {
      VS,
      FS,
      GS,
      TCS,
      TES
    }

    fn add_line_to_src(src: &mut String, line: &String, line_nb: usize) {
      if src.is_empty() {
        *src += &format!("#line {}\n{}\n", line_nb, line);
      }
    }

    match File::open(path.as_ref()) {
      Ok(fh) => {
        let buffered = BufReader::new(fh);
        let mut tcs_src = String::new();
        let mut tes_src = String::new();
        let mut vs_src = String::new();
        let mut gs_src = String::new();
        let mut fs_src = String::new();
        let mut current_stage: Option<CurrentStage> = None;

        for (line_nb, line) in buffered.lines().enumerate() {
          let line_nb = line_nb + 1;
          let line = line.unwrap();
          let trimmed = line.trim();

          if trimmed.starts_with("#vs") {
            if !vs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #vs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::VS);
          } else if trimmed.starts_with("#fs") {
            if !fs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #fs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::FS);
          } else if trimmed.starts_with("#gs") {
            if !gs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #gs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::GS);
          } else if trimmed.starts_with("#tcs") {
            if !tcs_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tcs sections", line_nb)));
            }

            current_stage = Some(CurrentStage::TCS);
          } else if trimmed.starts_with("#tes") {
            if !tes_src.is_empty() {
              return Err(LoadError::ParseFailed(format!("(line {}) several #tes sections", line_nb)));
            }

            current_stage = Some(CurrentStage::TES);
          } else if current_stage.is_none() && !trimmed.starts_with("//") {
            return Err(LoadError::ParseFailed(format!("(line {}) not in a shader stage nor a comment", line_nb)));
          }

          match current_stage {
            Some(CurrentStage::VS) => {
              add_line_to_src(&mut vs_src, &line, line_nb);
            },
            Some(CurrentStage::FS) => {
              add_line_to_src(&mut fs_src, &line, line_nb);
            },
            Some(CurrentStage::GS) => {
              add_line_to_src(&mut gs_src, &line, line_nb);
            },
            Some(CurrentStage::TCS) => {
              add_line_to_src(&mut tcs_src, &line, line_nb);
            },
            Some(CurrentStage::TES) => {
              add_line_to_src(&mut tes_src, &line, line_nb);
            },
            None => {}
          }
        }

        let (program, warnings) = try!(new_program(&tcs_src, &tes_src, &vs_src, &gs_src, &fs_src, &args)
          .map_err(|e| LoadError::ConversionFailed(format!("{:?}", e))));

        // check for semantic errors
        for warning in warnings {
          warn!("uniform warning: {:?}", warning);
        }

        Ok(
          Program {
            program: program,
            sem_map: args
          }
        )
      },
      Err(e) => {
        Err(LoadError::FileNotFound(path.as_ref().to_owned(), format!("{:?}", e)))
      }
    }
  }
}

impl Reload for Program {
  fn reload_args(&self) -> Self::Args {
    self.sem_map.clone()
  }
}

/// A helper function used to make uniforms optionable. If there’s a warning, it’s printed out.
pub fn opt_uni<T>(uni: (Uniform<T>, Option<UniformWarning>)) -> Uniform<T> where T: Uniformable<GL33> {
  if let Some(warning) = uni.1 {
    warn!("{:?}", warning);
  }

  uni.0
}
<|endoftext|>