<commit_before>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};

use std::fmt;
use std::path::{Path, PathBuf};

use super::utils::{get_sys_value_str, WrapMap};

#[doc(hidden)]
impl From<libc::c_char> for ProcessStatus {
    fn from(status: libc::c_char) -> ProcessStatus {
        match status {
            libc::SIDL => ProcessStatus::Idle,
            libc::SRUN => ProcessStatus::Run,
            libc::SSLEEP => ProcessStatus::Sleep,
            libc::SSTOP => ProcessStatus::Stop,
            libc::SZOMB => ProcessStatus::Zombie,
            libc::SWAIT => ProcessStatus::Dead,
            libc::SLOCK => ProcessStatus::LockBlocked,
            x => ProcessStatus::Unknown(x as _),
        }
    }
}

impl fmt::Display for ProcessStatus {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match *self {
            ProcessStatus::Idle => "Idle",
            ProcessStatus::Run => "Runnable",
            ProcessStatus::Sleep => "Sleeping",
            ProcessStatus::Stop => "Stopped",
            ProcessStatus::Zombie => "Zombie",
            ProcessStatus::Dead => "Dead",
            ProcessStatus::LockBlocked => "LockBlocked",
            _ => "Unknown",
        })
    }
}

#[doc = include_str!("../../md_doc/process.md")]
pub struct Process {
    pub(crate) name: String,
    pub(crate) cmd: Vec<String>,
    pub(crate) exe: PathBuf,
    pub(crate) pid: Pid,
    parent: Option<Pid>,
    pub(crate) environ: Vec<String>,
    pub(crate) cwd: PathBuf,
    pub(crate) root: PathBuf,
    pub(crate) memory: u64,
    pub(crate) virtual_memory: u64,
    pub(crate) updated: bool,
    cpu_usage: f32,
    start_time: u64,
    run_time: u64,
    pub(crate) status: ProcessStatus,
    user_id: Uid,
    group_id: Gid,
    read_bytes: u64,
    old_read_bytes: u64,
    written_bytes: u64,
    old_written_bytes: u64,
}

impl ProcessExt for Process {
    fn kill_with(&self, signal: Signal) -> Option<bool> {
        let c_signal = super::system::convert_signal(signal)?;
        unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn cmd(&self) -> &[String] {
        &self.cmd
    }

    fn exe(&self) -> &Path {
        self.exe.as_path()
    }

    fn pid(&self) -> Pid {
        self.pid
    }

    fn environ(&self) -> &[String] {
        &self.environ
    }

    fn cwd(&self) -> &Path {
        self.cwd.as_path()
    }

    fn root(&self) -> &Path {
        self.root.as_path()
    }

    fn memory(&self) -> u64 {
        self.memory
    }

    fn virtual_memory(&self) -> u64 {
        self.virtual_memory
    }

    fn parent(&self) -> Option<Pid> {
        self.parent
    }

    fn status(&self) -> ProcessStatus {
        self.status
    }

    fn start_time(&self) -> u64 {
        self.start_time
    }

    fn run_time(&self) -> u64 {
        self.run_time
    }

    fn cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn disk_usage(&self) -> DiskUsage {
        DiskUsage {
            written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
            total_written_bytes: self.written_bytes,
            read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
            total_read_bytes: self.read_bytes,
        }
    }

    fn user_id(&self) -> Option<&Uid> {
        Some(&self.user_id)
    }

    fn group_id(&self) -> Option<Gid> {
        Some(self.group_id)
    }
}

pub(crate) unsafe fn get_process_data(
    kproc: &libc::kinfo_proc,
    wrap: &WrapMap,
    page_size: isize,
    fscale: f32,
    now: u64,
    refresh_kind: ProcessRefreshKind,
) -> Result<Option<Process>, ()> {
    if kproc.ki_pid != 1 && (kproc.ki_flag as libc::c_int & libc::P_SYSTEM) != 0 {
        // We filter out the kernel threads.
        return Err(());
    }

    // We now get the values needed for both new and existing process.
    let cpu_usage = if refresh_kind.cpu() {
        (100 * kproc.ki_pctcpu) as f32 / fscale
    } else {
        0.
    };
    // Processes can be reparented apparently?
    let parent = if kproc.ki_ppid != 0 {
        Some(Pid(kproc.ki_ppid))
    } else {
        None
    };
    let status = ProcessStatus::from(kproc.ki_stat);

    // from FreeBSD source /src/usr.bin/top/machine.c
    let virtual_memory = (kproc.ki_size / 1_000) as u64;
    let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _) / 1_000;
    // FIXME: This is to get the "real" run time (in micro-seconds).
    // let run_time = (kproc.ki_runtime + 5_000) / 10_000;

    if let Some(proc_) = (*wrap.0.get()).get_mut(&Pid(kproc.ki_pid)) {
        proc_.cpu_usage = cpu_usage;
        proc_.parent = parent;
        proc_.status = status;
        proc_.virtual_memory = virtual_memory;
        proc_.memory = memory;
        proc_.run_time = now.saturating_sub(proc_.start_time);
        proc_.updated = true;

        if refresh_kind.disk_usage() {
            proc_.old_read_bytes = proc_.read_bytes;
            proc_.read_bytes = kproc.ki_rusage.ru_inblock as _;
            proc_.old_written_bytes = proc_.written_bytes;
            proc_.written_bytes = kproc.ki_rusage.ru_oublock as _;
        }

        return Ok(None);
    }

    // This is a new process, we need to get more information!
    let mut buffer = [0; libc::PATH_MAX as usize + 1];

    let exe = get_sys_value_str(
        &[
            libc::CTL_KERN,
            libc::KERN_PROC,
            libc::KERN_PROC_PATHNAME,
            kproc.ki_pid,
        ],
        &mut buffer,
    )
    .unwrap_or_default();
    // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use
    // procstat to get around this problem.
    // let cwd = get_sys_value_str(
    //     &[
    //         libc::CTL_KERN,
    //         libc::KERN_PROC,
    //         libc::KERN_PROC_CWD,
    //         kproc.ki_pid,
    //     ],
    //     &mut buffer,
    // )
    // .map(|s| s.into())
    // .unwrap_or_else(PathBuf::new);

    let start_time = kproc.ki_start.tv_sec as u64;
    Ok(Some(Process {
        pid: Pid(kproc.ki_pid),
        parent,
        user_id: Uid(kproc.ki_ruid),
        group_id: Gid(kproc.ki_rgid),
        start_time,
        run_time: now.saturating_sub(start_time),
        cpu_usage,
        virtual_memory,
        memory,
        // procstat_getfiles
        cwd: PathBuf::new(),
        exe: exe.into(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        name: String::new(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        cmd: Vec::new(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        root: PathBuf::new(),
        // kvm_getenvv isn't thread-safe so we get it in the main thread.
        environ: Vec::new(),
        status,
        read_bytes: kproc.ki_rusage.ru_inblock as _,
        old_read_bytes: 0,
        written_bytes: kproc.ki_rusage.ru_oublock as _,
        old_written_bytes: 0,
        updated: false,
    }))
}
<commit_msg>Correctly handle when a PID owner changes on FreeBSD<commit_after>// Take a look at the license at the top of the repository in the LICENSE file.

use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid};

use std::fmt;
use std::path::{Path, PathBuf};

use super::utils::{get_sys_value_str, WrapMap};

#[doc(hidden)]
impl From<libc::c_char> for ProcessStatus {
    fn from(status: libc::c_char) -> ProcessStatus {
        match status {
            libc::SIDL => ProcessStatus::Idle,
            libc::SRUN => ProcessStatus::Run,
            libc::SSLEEP => ProcessStatus::Sleep,
            libc::SSTOP => ProcessStatus::Stop,
            libc::SZOMB => ProcessStatus::Zombie,
            libc::SWAIT => ProcessStatus::Dead,
            libc::SLOCK => ProcessStatus::LockBlocked,
            x => ProcessStatus::Unknown(x as _),
        }
    }
}

impl fmt::Display for ProcessStatus {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(match *self {
            ProcessStatus::Idle => "Idle",
            ProcessStatus::Run => "Runnable",
            ProcessStatus::Sleep => "Sleeping",
            ProcessStatus::Stop => "Stopped",
            ProcessStatus::Zombie => "Zombie",
            ProcessStatus::Dead => "Dead",
            ProcessStatus::LockBlocked => "LockBlocked",
            _ => "Unknown",
        })
    }
}

#[doc = include_str!("../../md_doc/process.md")]
pub struct Process {
    pub(crate) name: String,
    pub(crate) cmd: Vec<String>,
    pub(crate) exe: PathBuf,
    pub(crate) pid: Pid,
    parent: Option<Pid>,
    pub(crate) environ: Vec<String>,
    pub(crate) cwd: PathBuf,
    pub(crate) root: PathBuf,
    pub(crate) memory: u64,
    pub(crate) virtual_memory: u64,
    pub(crate) updated: bool,
    cpu_usage: f32,
    start_time: u64,
    run_time: u64,
    pub(crate) status: ProcessStatus,
    user_id: Uid,
    group_id: Gid,
    read_bytes: u64,
    old_read_bytes: u64,
    written_bytes: u64,
    old_written_bytes: u64,
}

impl ProcessExt for Process {
    fn kill_with(&self, signal: Signal) -> Option<bool> {
        let c_signal = super::system::convert_signal(signal)?;
        unsafe { Some(libc::kill(self.pid.0, c_signal) == 0) }
    }

    fn name(&self) -> &str {
        &self.name
    }

    fn cmd(&self) -> &[String] {
        &self.cmd
    }

    fn exe(&self) -> &Path {
        self.exe.as_path()
    }

    fn pid(&self) -> Pid {
        self.pid
    }

    fn environ(&self) -> &[String] {
        &self.environ
    }

    fn cwd(&self) -> &Path {
        self.cwd.as_path()
    }

    fn root(&self) -> &Path {
        self.root.as_path()
    }

    fn memory(&self) -> u64 {
        self.memory
    }

    fn virtual_memory(&self) -> u64 {
        self.virtual_memory
    }

    fn parent(&self) -> Option<Pid> {
        self.parent
    }

    fn status(&self) -> ProcessStatus {
        self.status
    }

    fn start_time(&self) -> u64 {
        self.start_time
    }

    fn run_time(&self) -> u64 {
        self.run_time
    }

    fn cpu_usage(&self) -> f32 {
        self.cpu_usage
    }

    fn disk_usage(&self) -> DiskUsage {
        DiskUsage {
            written_bytes: self.written_bytes.saturating_sub(self.old_written_bytes),
            total_written_bytes: self.written_bytes,
            read_bytes: self.read_bytes.saturating_sub(self.old_read_bytes),
            total_read_bytes: self.read_bytes,
        }
    }

    fn user_id(&self) -> Option<&Uid> {
        Some(&self.user_id)
    }

    fn group_id(&self) -> Option<Gid> {
        Some(self.group_id)
    }
}

pub(crate) unsafe fn get_process_data(
    kproc: &libc::kinfo_proc,
    wrap: &WrapMap,
    page_size: isize,
    fscale: f32,
    now: u64,
    refresh_kind: ProcessRefreshKind,
) -> Result<Option<Process>, ()> {
    if kproc.ki_pid != 1 && (kproc.ki_flag as libc::c_int & libc::P_SYSTEM) != 0 {
        // We filter out the kernel threads.
        return Err(());
    }

    // We now get the values needed for both new and existing process.
    let cpu_usage = if refresh_kind.cpu() {
        (100 * kproc.ki_pctcpu) as f32 / fscale
    } else {
        0.
    };
    // Processes can be reparented apparently?
    let parent = if kproc.ki_ppid != 0 {
        Some(Pid(kproc.ki_ppid))
    } else {
        None
    };
    let status = ProcessStatus::from(kproc.ki_stat);

    // from FreeBSD source /src/usr.bin/top/machine.c
    let virtual_memory = (kproc.ki_size / 1_000) as u64;
    let memory = (kproc.ki_rssize as u64).saturating_mul(page_size as _) / 1_000;
    // FIXME: This is to get the "real" run time (in micro-seconds).
    // let run_time = (kproc.ki_runtime + 5_000) / 10_000;

    let start_time = kproc.ki_start.tv_sec as u64;

    if let Some(proc_) = (*wrap.0.get()).get_mut(&Pid(kproc.ki_pid)) {
        proc_.updated = true;
        // If the `start_time` we just got is different from the one stored, it means it's not the
        // same process.
        if proc_.start_time == start_time {
            proc_.cpu_usage = cpu_usage;
            proc_.parent = parent;
            proc_.status = status;
            proc_.virtual_memory = virtual_memory;
            proc_.memory = memory;
            proc_.run_time = now.saturating_sub(proc_.start_time);

            if refresh_kind.disk_usage() {
                proc_.old_read_bytes = proc_.read_bytes;
                proc_.read_bytes = kproc.ki_rusage.ru_inblock as _;
                proc_.old_written_bytes = proc_.written_bytes;
                proc_.written_bytes = kproc.ki_rusage.ru_oublock as _;
            }

            return Ok(None);
        }
    }

    // This is a new process, we need to get more information!
    let mut buffer = [0; libc::PATH_MAX as usize + 1];

    let exe = get_sys_value_str(
        &[
            libc::CTL_KERN,
            libc::KERN_PROC,
            libc::KERN_PROC_PATHNAME,
            kproc.ki_pid,
        ],
        &mut buffer,
    )
    .unwrap_or_default();
    // For some reason, it can return completely invalid path like `p\u{5}`. So we need to use
    // procstat to get around this problem.
    // let cwd = get_sys_value_str(
    //     &[
    //         libc::CTL_KERN,
    //         libc::KERN_PROC,
    //         libc::KERN_PROC_CWD,
    //         kproc.ki_pid,
    //     ],
    //     &mut buffer,
    // )
    // .map(|s| s.into())
    // .unwrap_or_else(PathBuf::new);

    Ok(Some(Process {
        pid: Pid(kproc.ki_pid),
        parent,
        user_id: Uid(kproc.ki_ruid),
        group_id: Gid(kproc.ki_rgid),
        start_time,
        run_time: now.saturating_sub(start_time),
        cpu_usage,
        virtual_memory,
        memory,
        // procstat_getfiles
        cwd: PathBuf::new(),
        exe: exe.into(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        name: String::new(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        cmd: Vec::new(),
        // kvm_getargv isn't thread-safe so we get it in the main thread.
        root: PathBuf::new(),
        // kvm_getenvv isn't thread-safe so we get it in the main thread.
        environ: Vec::new(),
        status,
        read_bytes: kproc.ki_rusage.ru_inblock as _,
        old_read_bytes: 0,
        written_bytes: kproc.ki_rusage.ru_oublock as _,
        old_written_bytes: 0,
        updated: false,
    }))
}
<|endoftext|>


<commit_before>use state::State;
use context::BaseContext;

use self::message::Action as MessageAction;
use self::message::Command as MessageCommand;
pub use self::message::MessageActionHandler;

#[derive(Debug)]
pub enum Action {
    Message(MessageAction)
}

impl Action {
    pub fn execute(&self, state: &State, context: &BaseContext) -> ActionCommand {
        match *self {
            Action::Message(ref action) => action.execute(state, context)
        }
    }
}

pub enum ActionCommand {
    Message(MessageCommand)
}

pub struct ActionHandlers {
    message_handler: Box<MessageActionHandler>
}

impl ActionHandlers {
    pub fn handle(&mut self, command: ActionCommand) {
        match command {
            ActionCommand::Message(message) => self.message_handler.handle(message)
        }
    }
}

pub mod message {
    use context::BaseContext;
    use state::State;
    use Message;
    use super::ActionCommand;

    #[derive(Debug)]
    pub struct Action;

    impl Action {
        pub fn execute(&self, _: &State, _: &BaseContext) -> ActionCommand {
            ActionCommand::Message(Command(Message::new("".to_string())))
        }
    }

    pub struct Command(Message);

    pub trait MessageActionHandler {
        fn handle(&mut self, command: Command);
    }
}
<commit_msg>ActionCommand: derive Debug implementation<commit_after>use state::State;
use context::BaseContext;

use self::message::Action as MessageAction;
use self::message::Command as MessageCommand;
pub use self::message::MessageActionHandler;

#[derive(Debug)]
pub enum Action {
    Message(MessageAction)
}

impl Action {
    pub fn execute(&self, state: &State, context: &BaseContext) -> ActionCommand {
        match *self {
            Action::Message(ref action) => action.execute(state, context)
        }
    }
}

#[derive(Debug)]
pub enum ActionCommand {
    Message(MessageCommand)
}

pub struct ActionHandlers {
    message_handler: Box<MessageActionHandler>
}

impl ActionHandlers {
    pub fn handle(&mut self, command: ActionCommand) {
        match command {
            ActionCommand::Message(message) => self.message_handler.handle(message)
        }
    }
}

pub mod message {
    use context::BaseContext;
    use state::State;
    use Message;
    use super::ActionCommand;

    #[derive(Debug)]
    pub struct Action;

    impl Action {
        pub fn execute(&self, _: &State, _: &BaseContext) -> ActionCommand {
            ActionCommand::Message(Command(Message::new("".to_string())))
        }
    }

    #[derive(Debug)]
    pub struct Command(Message);

    pub trait MessageActionHandler {
        fn handle(&mut self, command: Command);
    }
}
<|endoftext|>


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

use super::Term;

pub fn handle(term: &mut Term, ch: char) {
    match ch {
        '\x1b' => { // Escape key
            term.editor.switch_mode();
        }
        '\x7f' => { // Backspace key
            let line = term.current_line() + term.cursor.pos.y;
            let pos = term.cursor.pos.x;
            term.cursor.save_pos();
            if term.cursor.pos.x == 0 {
                term.editor.contents.remove(line);
                if term.cursor.pos.y != 0 {
                    term.cursor.pos.y -= 1;
                }

                let line = term.current_line() + term.cursor.pos.y;
                let len = term.editor.contents[line].len() - 1;
                term.cursor.pos.x = len;
                term.redraw_file();
            } else {
                term.cursor.pos.x -= 1;
            }
        }
        '\r' => {
            let line = term.current_line() + term.cursor.pos.y;
            term.editor.contents.insert(line, vec![b'\n']);
            term.cursor.save_pos();
            term.cursor.pos.x = 0;
            term.cursor.pos.y += 1;
            term.redraw_file();
        }
        '\t' => {
            for i in 0..4 {
                let x = term.cursor.pos.x + i;
                term.term[(x, term.cursor.pos.y)].set_ch(' ');
            }
            term.cursor.save_pos();
            term.cursor.pos.x += 4;
        }
        c @ _ => {
            /*let line = term.current_line() + term.cursor.pos.y;
            let pos = term.cursor.pos.x;
            term.editor.contents[line, pos*/
            term.term[term.cursor.current_pos()].set_ch(c);
            term.cursor.save_pos();
            term.cursor.pos.x += 1;
        }
    }
}
<commit_msg>Backspace in line implemented<commit_after>/* Copyright (C)  2016 nokaa <nokaa@cock.li>
 * This software is licensed under the terms of the
 * GNU Affero General Public License. You should have
 * received a copy of this license with this software.
 * The license may also be found at https://gnu.org/licenses/agpl.txt
 */

use super::Term;

pub fn handle(term: &mut Term, ch: char) {
    match ch {
        '\x1b' => { // Escape key
            term.editor.switch_mode();
        }
        '\x7f' => { // Backspace key
            let line = term.current_line() + term.cursor.pos.y;
            term.cursor.save_pos();
            if term.cursor.pos.x == 0 {
                term.editor.contents.remove(line);
                if term.cursor.pos.y != 0 {
                    term.cursor.pos.y -= 1;
                }

                let line = term.current_line() + term.cursor.pos.y;
                let len = term.editor.contents[line].len() - 1;
                term.cursor.pos.x = len;
                term.redraw_file();
            } else {
                term.cursor.pos.x -= 1;
                let pos = term.cursor.pos.x;
                term.editor.contents[line].remove(pos);
                term.redraw_file();
            }
        }
        '\r' => {
            let line = term.current_line() + term.cursor.pos.y;
            term.editor.contents.insert(line, vec![b'\n']);
            term.cursor.save_pos();
            term.cursor.pos.x = 0;
            term.cursor.pos.y += 1;
            term.redraw_file();
        }
        '\t' => {
            for i in 0..4 {
                let x = term.cursor.pos.x + i;
                term.term[(x, term.cursor.pos.y)].set_ch(' ');
            }
            term.cursor.save_pos();
            term.cursor.pos.x += 4;
        }
        c @ _ => {
            /*let line = term.current_line() + term.cursor.pos.y;
            let pos = term.cursor.pos.x;
            term.editor.contents[line, pos*/
            term.term[term.cursor.current_pos()].set_ch(c);
            term.cursor.save_pos();
            term.cursor.pos.x += 1;
        }
    }
}
<|endoftext|>


<commit_before>use crate::audio::SoundSource;
use crate::tests;
use crate::*;

#[test]
fn audio_load_ogg() {
    let (c, _e) = &mut tests::make_context();

    // OGG format
    let _sound = audio::Source::new(c, "/pew.ogg").unwrap();
    let _sound = audio::SpatialSource::new(c, "/pew.ogg").unwrap();
}

#[test]
fn audio_load_wav() {
    let (c, _e) = &mut tests::make_context();

    // WAV format
    let _sound = audio::Source::new(c, "/pew.wav").unwrap();
    let _sound = audio::SpatialSource::new(c, "/pew.wav").unwrap();
}

#[test]
fn audio_load_flac() {
    let (c, _e) = &mut tests::make_context();

    // FLAC format
    let _sound = audio::Source::new(c, "/pew.flac").unwrap();
    let _sound = audio::SpatialSource::new(c, "/pew.flac").unwrap();
}

#[test]
fn fail_when_loading_nonexistent_file() {
    let (c, _e) = &mut tests::make_context();

    // File does not exist
    assert!(audio::Source::new(c, "/does-not-exists.ogg").is_err());
    assert!(audio::SpatialSource::new(c, "/does-not-exists.ogg").is_err());
}

#[test]
fn fail_when_loading_non_audio_file() {
    let (c, _e) = &mut tests::make_context();

    assert!(audio::Source::new(c, "/player.png").is_err());
    assert!(audio::SpatialSource::new(c, "/player.png").is_err());
}

#[test]
fn volume_persists_after_stop() {
    let (c, _e) = &mut tests::make_context();
    {
        let volume = 0.8;
        let mut sound = audio::Source::new(c, "/pew.ogg").unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.stop();
        assert_eq!(sound.volume(), volume);
    }

    {
        let volume = 0.8;
        let mut sound = audio::SpatialSource::new(c, "/pew.ogg").unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.stop();
        assert_eq!(sound.volume(), volume);
    }
}

#[test]
fn volume_persists_after_play() {
    let (c, _e) = &mut tests::make_context();
    {
        let volume = 0.8;
        let mut sound = audio::Source::new(c, "/pew.ogg").unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.play().unwrap();
        assert_eq!(sound.volume(), volume);
    }

    {
        let volume = 0.8;
        let mut sound = audio::SpatialSource::new(c, "/pew.ogg").unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.play().unwrap();
        assert_eq!(sound.volume(), volume);
    }
}
<commit_msg>Moved filename in to variable to avoid typo issues<commit_after>use crate::audio::SoundSource;
use crate::tests;
use crate::*;

#[test]
fn audio_load_ogg() {
    let (c, _e) = &mut tests::make_context();

    // OGG format
    let filename = "/pew.ogg";
    let _sound = audio::Source::new(c, filename).unwrap();
    let _sound = audio::SpatialSource::new(c, filename).unwrap();
}

#[test]
fn audio_load_wav() {
    let (c, _e) = &mut tests::make_context();

    // WAV format
    let filename = "/pew.wav";
    let _sound = audio::Source::new(c, filename).unwrap();
    let _sound = audio::SpatialSource::new(c, filename).unwrap();
}

#[test]
fn audio_load_flac() {
    let (c, _e) = &mut tests::make_context();

    // FLAC format
    let filename = "/pew.flac";
    let _sound = audio::Source::new(c, filename).unwrap();
    let _sound = audio::SpatialSource::new(c, filename).unwrap();
}

#[test]
fn fail_when_loading_nonexistent_file() {
    let (c, _e) = &mut tests::make_context();

    // File does not exist
    let filename = "/does-not-exist.ogg";
    assert!(audio::Source::new(c, filename).is_err());
    assert!(audio::SpatialSource::new(c, filename).is_err());
}

#[test]
fn fail_when_loading_non_audio_file() {
    let (c, _e) = &mut tests::make_context();

    let filename = "/player.png";
    assert!(audio::Source::new(c, filename).is_err());
    assert!(audio::SpatialSource::new(c, filename).is_err());
}

#[test]
fn volume_persists_after_stop() {
    let (c, _e) = &mut tests::make_context();
    let filename = "/pew.ogg";
    {
        let volume = 0.8;
        let mut sound = audio::Source::new(c, filename).unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.stop();
        assert_eq!(sound.volume(), volume);
    }

    {
        let volume = 0.8;
        let mut sound = audio::SpatialSource::new(c, filename).unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.stop();
        assert_eq!(sound.volume(), volume);
    }
}

#[test]
fn volume_persists_after_play() {
    let (c, _e) = &mut tests::make_context();
    let filename = "/pew.ogg";
    {
        let volume = 0.8;
        let mut sound = audio::Source::new(c, filename).unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.play().unwrap();
        assert_eq!(sound.volume(), volume);
    }

    {
        let volume = 0.8;
        let mut sound = audio::SpatialSource::new(c, filename).unwrap();
        sound.set_volume(volume);
        assert_eq!(sound.volume(), volume);
        sound.play().unwrap();
        assert_eq!(sound.volume(), volume);
    }
}
<|endoftext|>


<commit_before>// NOTE: This may not work properly/at all. The compiler
// is the focus. It exist mainly for debugging: since it uses
// the same parser, it can separate parser/optimizer bugs from compiler
// bugs.

use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

const BUFSIZE: usize = 8192;

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(code.as_str());
    let tokens = optimize(tokens);

    let mut i: usize = 0;
    let mut loops: Vec<usize> = Vec::new();
    let mut mem: [i32; BUFSIZE] = [0; BUFSIZE];
    let mut cur = BUFSIZE/2;
    let mut outbuff = String::new();
    while i < tokens.len() - 1 {
        let mut token = tokens.get(i).unwrap();
        match *token {
            Add(offset, value) =>
                mem[(cur as i32 + offset) as usize] += value,
            MulCopy(src, dest, mul) =>
                mem[(cur as i32 + dest) as usize] += mem[(cur as i32 + src) as usize]*mul,
            Set(offset, value) =>
                mem[(cur as i32 + offset) as usize] = value,
            Move(offset) =>
                cur = (cur as i32 + offset) as usize,
            Loop =>
                if mem[cur] != 0 {
                    loops.push(i);
                } else {
                    let mut skiploop = 1;
                    while i < tokens.len() && skiploop > 0 {
                        i += 1;
                        token = tokens.get(i).unwrap();
                        if *token == EndLoop {
                            skiploop -= 1;
                        } else if *token == Loop {
                            skiploop += 1;
                        }
                    }
                },
            EndLoop =>
                if mem[cur] != 0 {
                    i = *loops.last().unwrap() as usize;
                } else {
                    loops.pop().unwrap();
                },
            If(offset) =>
                if mem[(cur as i32 + offset) as usize] == 0 {
                    let mut skipif = 1;
                    while i < tokens.len() && skipif > 0 {
                        i += 1;
                        token = tokens.get(i).unwrap();
                        if *token == EndIf {
                            skipif -= 1;
                        } else if let If(_) = *token {
                            skipif += 1;
                        }
                    }
                },
            EndIf => {},
            Scan(offset) =>
                while mem[cur] != 0 {
                    cur = (cur as i32 + offset) as usize;
                },
            Input => {
                let mut buffer = [0; 1];
                io::stdin().take(1).read(&mut buffer).unwrap();
                mem[cur] = buffer[0] as i32;
            },
            LoadOut(offset, add) =>
                outbuff.push((mem[(cur as i32 + offset) as usize] + add) as u8 as char),
            LoadOutSet(value) =>
                outbuff.push(value as u8 as char),
            Output => {
                io::stdout().write(outbuff.as_bytes()).unwrap();
                io::stdout().flush().unwrap();
                outbuff.clear();
            }
        }
        i += 1;
    }
}
<commit_msg>Remove unneeded conversion<commit_after>// NOTE: This may not work properly/at all. The compiler
// is the focus. It exist mainly for debugging: since it uses
// the same parser, it can separate parser/optimizer bugs from compiler
// bugs.

use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;

extern crate isbfc;
use isbfc::Token::*;
use isbfc::parse;
use isbfc::optimize;

const BUFSIZE: usize = 8192;

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    let tokens = parse(code.as_str());
    let tokens = optimize(tokens);

    let mut i: usize = 0;
    let mut loops: Vec<usize> = Vec::new();
    let mut mem: [i32; BUFSIZE] = [0; BUFSIZE];
    let mut cur = BUFSIZE/2;
    let mut outbuff = String::new();
    while i < tokens.len() - 1 {
        let mut token = tokens.get(i).unwrap();
        match *token {
            Add(offset, value) =>
                mem[(cur as i32 + offset) as usize] += value,
            MulCopy(src, dest, mul) =>
                mem[(cur as i32 + dest) as usize] += mem[(cur as i32 + src) as usize]*mul,
            Set(offset, value) =>
                mem[(cur as i32 + offset) as usize] = value,
            Move(offset) =>
                cur = (cur as i32 + offset) as usize,
            Loop =>
                if mem[cur] != 0 {
                    loops.push(i);
                } else {
                    let mut skiploop = 1;
                    while i < tokens.len() && skiploop > 0 {
                        i += 1;
                        token = tokens.get(i).unwrap();
                        if *token == EndLoop {
                            skiploop -= 1;
                        } else if *token == Loop {
                            skiploop += 1;
                        }
                    }
                },
            EndLoop =>
                if mem[cur] != 0 {
                    i = *loops.last().unwrap();
                } else {
                    loops.pop().unwrap();
                },
            If(offset) =>
                if mem[(cur as i32 + offset) as usize] == 0 {
                    let mut skipif = 1;
                    while i < tokens.len() && skipif > 0 {
                        i += 1;
                        token = tokens.get(i).unwrap();
                        if *token == EndIf {
                            skipif -= 1;
                        } else if let If(_) = *token {
                            skipif += 1;
                        }
                    }
                },
            EndIf => {},
            Scan(offset) =>
                while mem[cur] != 0 {
                    cur = (cur as i32 + offset) as usize;
                },
            Input => {
                let mut buffer = [0; 1];
                io::stdin().take(1).read(&mut buffer).unwrap();
                mem[cur] = buffer[0] as i32;
            },
            LoadOut(offset, add) =>
                outbuff.push((mem[(cur as i32 + offset) as usize] + add) as u8 as char),
            LoadOutSet(value) =>
                outbuff.push(value as u8 as char),
            Output => {
                io::stdout().write(outbuff.as_bytes()).unwrap();
                io::stdout().flush().unwrap();
                outbuff.clear();
            }
        }
        i += 1;
    }
}
<|endoftext|>


<commit_before>use winapi::um::wincodec::{IWICImagingFactory, IWICBitmapDecoder, IWICBitmapSource, WICPixelFormatGUID};
use winapi::shared::winerror::S_OK;
use crate::win32::image_decoder as img;
use crate::{NwgError, Bitmap};
use std::{ptr, mem};
use std::ops::Deref;

static EMPTY_VEC: Vec<u8> = vec![];

pub struct LifetimeRef<'a, T: 'a> {
    owned_ref: &'a mut T,
}

impl<'a, T> Deref for LifetimeRef<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        let r: &'a T = unsafe { mem::transmute_copy(&self.owned_ref) };
        r
    }
}

impl<'a, T> From<T> for LifetimeRef<'a, T> {
    fn from(value: T) -> Self {
        let boxed = Box::new(value);
        Self { owned_ref: Box::leak(boxed) }
    }
}

impl<'a, T> Drop for LifetimeRef<'a, T> {
    fn drop(&mut self) {
        let boxed = unsafe { Box::from_raw(self.owned_ref as *mut T) };
        drop(boxed);
    }
}

/**
    A image decoder. Can load an extended number of image file format from a filename, from a file handle, or from a stream.

    ImageDecoder do not take any parameter to build, but it still provides a builder API to match the other component of NWG.
    You can also use "ImageDecoder::new" to avoid the builder API.

    There's not much reason to have more than 1 image decoder per application.

    Images loaded from a decoder cannot be used as-is by an image frame. They must first be converted to a bitmap resource.

    ```rust
    use native_windows_gui as nwg;
    fn open_image(decoder: &nwg::ImageDecoder) -> Result<nwg::ImageData, nwg::NwgError> {
        decoder
            .from_filename("corn.png")?
            .frame(0)
    }
    ```

    ```rust
    use native_windows_gui as nwg;
    fn build_image_decode(decode: &mut nwg::ImageDecoder) -> Result<(), nwg::NwgError> {
        nwg::ImageDecoder::builder()
            .build(decode)
    }
    ```
*/
pub struct ImageDecoder {
    pub factory: *mut IWICImagingFactory,
}

impl ImageDecoder {
    pub fn new() -> Result<ImageDecoder, NwgError> {
        let factory = unsafe { img::create_image_factory() }?;
        Ok(ImageDecoder { factory })
    }

    pub fn builder() -> ImageDecoderBuilder {
        ImageDecoderBuilder {
        }
    }

    /**
        Try to read an image from a file path.
        The file type can be any of the native WIC codecs (https://docs.microsoft.com/en-us/windows/win32/wic/native-wic-codecs)

        * If there is an error during the decoding, returns a NwgError.
        * If the image decoder was not initialized, this method panics

        This method returns a ImageSource object
    */
    pub fn from_filename<'a, 'b>(&self, path: &'a str) -> Result<LifetimeRef<'b, ImageSource>, NwgError> {
        if self.factory.is_null() {
            panic!("ImageDecoder is not yet bound to a winapi object");
        }

        let decoder = unsafe { img::create_decoder_from_file(&*self.factory, path) }?;

        Ok(LifetimeRef::from(ImageSource { decoder, stream: EMPTY_VEC.as_slice() }))
    }

    /**
        Build an image from a stream of data.
        The file type can be any of the native WIC codecs (https://docs.microsoft.com/en-us/windows/win32/wic/native-wic-codecs)

        * If there is an error during the decoding, returns a NwgError.
        * If the image decoder was not initialized, this method panics

        This method copies the bytes and returns a ImageSource object
    */
    pub fn from_stream<'a>(&self, stream: &'a mut [u8]) -> Result<LifetimeRef<'a, ImageSource<'a>>, NwgError> {
        if self.factory.is_null() {
            panic!("ImageDecoder is not yet bound to a winapi object");
        }

        let decoder = unsafe { img::create_decoder_from_stream(&*self.factory, stream) }?;

        Ok(LifetimeRef::from(ImageSource { decoder, stream }))
    }

    /**
        Resize an image, returning the new resized image. The pixel format might change.
    */
    pub fn resize_image(&self, image: &ImageData, new_size: [u32;2]) -> Result<ImageData, NwgError> {
        unsafe { img::resize_bitmap(&*self.factory, image, new_size) }
    }

}

/**
    Represents a image data source in read only mode.
*/
pub struct ImageSource<'a> {
    pub decoder: *mut IWICBitmapDecoder,
    #[allow(dead_code)]
    stream: &'a [u8],
}

impl<'a> ImageSource<'a> {

    /**
        Return the number of frame in the image. For most format (ex: PNG), this will be 1.
        It might be more than 1 in animated image formats (such as GIFs).
    */
    pub fn frame_count(&self) -> u32 {
        let mut frame_count = 0;
        unsafe { (&*self.decoder).GetFrameCount(&mut frame_count); }
        frame_count
    }

    /**
        Return the image data of the requested frame in a ImageData object.
    */
    pub fn frame<'b>(&self, index: u32) -> Result<LifetimeRef<'b, ImageData>, NwgError> where 'a : 'b {
        let mut bitmap = ptr::null_mut();
        let hr = unsafe { (&*self.decoder).GetFrame(index, &mut bitmap) };
        match hr {
            S_OK => Ok(LifetimeRef::from(ImageData { frame: bitmap as *mut IWICBitmapSource })),
            err => Err(NwgError::image_decoder(err, "Could not read image frame"))
        }
    }

    /*  Retrieves the container format of the image source.

        See https://docs.microsoft.com/en-us/windows/win32/wic/-wic-guids-clsids#container-formats
    */
    pub fn container_format(&self) -> ContainerFormat {
        use ContainerFormat::*;
        let mut container = unsafe { mem::zeroed() };
        unsafe { (&*self.decoder).GetContainerFormat(&mut container) };

        // Just use the first field of the GUID because the winapi type does no implement EQ
        match container.Data1 {
            0xf3ff6d0d => Adng,
            0xaf1d87e => Bmp,
            0x1b7cfaf4 => Png,
            0xa3a860c4 => Ico,
            0x19e4a5aa => Jpeg,
            0x163bcc30 => Tiff,
            0x1f8a5601 => Gif,
            0x57a37caa => Wmp,
            _ => Unknown
        }

    }

}

/**
    Represents a source of pixel that can be read, but cannot be written back to.
*/
pub struct ImageData {
    pub frame: *mut IWICBitmapSource
}

impl ImageData {

    /// Retrieves the sampling rate between pixels and physical world measurements.
    pub fn resolution(&self) -> (f64, f64) {
        let (mut rx, mut ry) = (0.0, 0.0);
        unsafe { (&*self.frame).GetResolution(&mut rx, &mut ry) };
        (rx, ry)
    }

    /// Retrieves the pixel width and height of the bitmap.
    pub fn size(&self) -> (u32, u32) {
        let (mut sx, mut sy) = (0, 0);
        unsafe { (&*self.frame).GetSize(&mut sx, &mut sy) };
        (sx, sy)
    }

    /*  Retrieves the pixel format of the bitmap source. Returns a GUID, Match it to the GUID defined in the link below:

        See https://docs.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats#undefined-pixel-formats
    */
    pub fn pixel_format(&self) -> WICPixelFormatGUID {
        let mut fmt = unsafe { mem::zeroed() };
        unsafe { (&*self.frame).GetPixelFormat(&mut fmt) };
        fmt
    }

    /**
        Copy the frame pixels into a buffer.

        Parameters:
            pixel_size: defines the size of a pixel in bytes. In a typical RGBA image, this would be 4 (1 byte for each component).
                        If unsure, use the pixel_format.

        May return an error if the pixel data could not be read
    */
    pub fn pixels(&self, pixel_size: u32) -> Result<Vec<u8>, NwgError> {
        let (w, h) = self.size();
        let scanline = w * pixel_size;
        let buffer_size = (w * h * pixel_size) as usize;
        let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);

        let hr = unsafe {
            buffer.set_len(buffer_size);
            (&*self.frame).CopyPixels(ptr::null(), scanline, buffer_size as u32, buffer.as_mut_ptr())
        };

        match hr {
            S_OK => Ok(buffer),
            err => Err(NwgError::image_decoder(err, "Could not read image pixels"))
        }
    }

    /**
        Copy a region of the frames pixel into a buffer.

        Parameters:
            offset: The [x,y] offset at which the region begins
            size: The [width, height] size of the region
            pixel_size: defines the size of a pixel in bytes. In a typical RGBA image, this would be 4 (1 byte for each component).
                        If unsure, use the pixel_format.

        May return an error if the pixel data could not be read
    */
    pub fn region_pixels(&self, offset: [i32;2], size: [i32;2], pixel_size: u32) -> Result<Vec<u8>, NwgError> {
        use winapi::um::wincodec::WICRect;

        let [x, y] = offset;
        let [w, h] = size;
        let scanline = (w as u32) * pixel_size;
        let buffer_size = (scanline * (h as u32)) as usize;
        let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);

        let region = WICRect { X: x, Y: y, Width: w, Height: h };

        let hr = unsafe {
            buffer.set_len(buffer_size);
            (&*self.frame).CopyPixels(&region, scanline, buffer_size as u32, buffer.as_mut_ptr())
        };

        match hr {
            S_OK => Ok(buffer),
            err => Err(NwgError::image_decoder(err, "Could not read image pixels"))
        }
    }

    /**
        Create a bitmap resource the the image data. This resource can then be used in the other NWG component.
        The bitmap returned is considered "owned".
    */
    pub fn as_bitmap(&self) -> Result<Bitmap, NwgError> {
        unsafe { img::create_bitmap_from_wic(self) }
    }

}

/// A list of container format implemented in WIC
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ContainerFormat {
    Unknown,
    Adng,
    Bmp,
    Png,
    Ico,
    Jpeg,
    Tiff,
    Gif,
    Wmp,
}

//
// IMPL
//

impl Default for ImageDecoder {
    fn default() -> ImageDecoder {
        ImageDecoder {
            factory: ptr::null_mut()
        }
    }
}

impl Drop for ImageDecoder {
    fn drop(&mut self) {
        if !self.factory.is_null() {
            unsafe { (&*self.factory).Release(); }
        }
    }
}

impl<'a> Drop for ImageSource<'a> {
    fn drop(&mut self) {
        unsafe { (&*self.decoder).Release(); }
    }
}

impl Drop for ImageData {
    fn drop(&mut self) {
        unsafe { (&*self.frame).Release(); }
    }
}

/**
    A blank builder for the image decoder
*/
pub struct ImageDecoderBuilder {
}

impl ImageDecoderBuilder {
    pub fn build(self, out: &mut ImageDecoder) -> Result<(), NwgError> {
        let factory = unsafe { img::create_image_factory() }?;
        *out = ImageDecoder { factory };
        Ok(())
    }
}
<commit_msg>Just a little more...<commit_after>use winapi::um::wincodec::{IWICImagingFactory, IWICBitmapDecoder, IWICBitmapSource, WICPixelFormatGUID};
use winapi::shared::winerror::S_OK;
use crate::win32::image_decoder as img;
use crate::{NwgError, Bitmap};
use std::{ptr, mem};
use std::ops::Deref;

static EMPTY_VEC: Vec<u8> = vec![];

pub struct LifetimeRef<'a, T: 'a> {
    owned_ref: &'a mut T,
}

impl<'a, T> Deref for LifetimeRef<'a, T> {
    type Target = T;
    fn deref(&self) -> &T { self.owned_ref }
}

impl<'a, T> From<T> for LifetimeRef<'a, T> {
    fn from(value: T) -> Self {
        let boxed = Box::new(value);
        Self { owned_ref: Box::leak(boxed) }
    }
}

impl<'a, T> Drop for LifetimeRef<'a, T> {
    fn drop(&mut self) {
        let boxed = unsafe { Box::from_raw(self.owned_ref as *mut T) };
        drop(boxed);
    }
}

/**
    A image decoder. Can load an extended number of image file format from a filename, from a file handle, or from a stream.

    ImageDecoder do not take any parameter to build, but it still provides a builder API to match the other component of NWG.
    You can also use "ImageDecoder::new" to avoid the builder API.

    There's not much reason to have more than 1 image decoder per application.

    Images loaded from a decoder cannot be used as-is by an image frame. They must first be converted to a bitmap resource.

    ```rust
    use native_windows_gui as nwg;
    fn open_image(decoder: &nwg::ImageDecoder) -> Result<nwg::ImageData, nwg::NwgError> {
        decoder
            .from_filename("corn.png")?
            .frame(0)
    }
    ```

    ```rust
    use native_windows_gui as nwg;
    fn build_image_decode(decode: &mut nwg::ImageDecoder) -> Result<(), nwg::NwgError> {
        nwg::ImageDecoder::builder()
            .build(decode)
    }
    ```
*/
pub struct ImageDecoder {
    pub factory: *mut IWICImagingFactory,
}

impl ImageDecoder {
    pub fn new() -> Result<ImageDecoder, NwgError> {
        let factory = unsafe { img::create_image_factory() }?;
        Ok(ImageDecoder { factory })
    }

    pub fn builder() -> ImageDecoderBuilder {
        ImageDecoderBuilder {
        }
    }

    /**
        Try to read an image from a file path.
        The file type can be any of the native WIC codecs (https://docs.microsoft.com/en-us/windows/win32/wic/native-wic-codecs)

        * If there is an error during the decoding, returns a NwgError.
        * If the image decoder was not initialized, this method panics

        This method returns a ImageSource object
    */
    pub fn from_filename<'a, 'b>(&self, path: &'a str) -> Result<LifetimeRef<'b, ImageSource>, NwgError> {
        if self.factory.is_null() {
            panic!("ImageDecoder is not yet bound to a winapi object");
        }

        let decoder = unsafe { img::create_decoder_from_file(&*self.factory, path) }?;

        Ok(LifetimeRef::from(ImageSource { decoder, stream: EMPTY_VEC.as_slice() }))
    }

    /**
        Build an image from a stream of data.
        The file type can be any of the native WIC codecs (https://docs.microsoft.com/en-us/windows/win32/wic/native-wic-codecs)

        * If there is an error during the decoding, returns a NwgError.
        * If the image decoder was not initialized, this method panics

        This method copies the bytes and returns a ImageSource object
    */
    pub fn from_stream<'a>(&self, stream: &'a mut [u8]) -> Result<LifetimeRef<'a, ImageSource<'a>>, NwgError> {
        if self.factory.is_null() {
            panic!("ImageDecoder is not yet bound to a winapi object");
        }

        let decoder = unsafe { img::create_decoder_from_stream(&*self.factory, stream) }?;

        Ok(LifetimeRef::from(ImageSource { decoder, stream }))
    }

    /**
        Resize an image, returning the new resized image. The pixel format might change.
    */
    pub fn resize_image(&self, image: &ImageData, new_size: [u32;2]) -> Result<ImageData, NwgError> {
        unsafe { img::resize_bitmap(&*self.factory, image, new_size) }
    }

}

/**
    Represents a image data source in read only mode.
*/
pub struct ImageSource<'a> {
    pub decoder: *mut IWICBitmapDecoder,
    #[allow(dead_code)]
    stream: &'a [u8],
}

impl<'a> ImageSource<'a> {

    /**
        Return the number of frame in the image. For most format (ex: PNG), this will be 1.
        It might be more than 1 in animated image formats (such as GIFs).
    */
    pub fn frame_count(&self) -> u32 {
        let mut frame_count = 0;
        unsafe { (&*self.decoder).GetFrameCount(&mut frame_count); }
        frame_count
    }

    /**
        Return the image data of the requested frame in a ImageData object.
    */
    pub fn frame<'b>(&self, index: u32) -> Result<LifetimeRef<'b, ImageData>, NwgError> where 'a : 'b {
        let mut bitmap = ptr::null_mut();
        let hr = unsafe { (&*self.decoder).GetFrame(index, &mut bitmap) };
        match hr {
            S_OK => Ok(LifetimeRef::from(ImageData { frame: bitmap as *mut IWICBitmapSource })),
            err => Err(NwgError::image_decoder(err, "Could not read image frame"))
        }
    }

    /*  Retrieves the container format of the image source.

        See https://docs.microsoft.com/en-us/windows/win32/wic/-wic-guids-clsids#container-formats
    */
    pub fn container_format(&self) -> ContainerFormat {
        use ContainerFormat::*;
        let mut container = unsafe { mem::zeroed() };
        unsafe { (&*self.decoder).GetContainerFormat(&mut container) };

        // Just use the first field of the GUID because the winapi type does no implement EQ
        match container.Data1 {
            0xf3ff6d0d => Adng,
            0xaf1d87e => Bmp,
            0x1b7cfaf4 => Png,
            0xa3a860c4 => Ico,
            0x19e4a5aa => Jpeg,
            0x163bcc30 => Tiff,
            0x1f8a5601 => Gif,
            0x57a37caa => Wmp,
            _ => Unknown
        }

    }

}

/**
    Represents a source of pixel that can be read, but cannot be written back to.
*/
pub struct ImageData {
    pub frame: *mut IWICBitmapSource
}

impl ImageData {

    /// Retrieves the sampling rate between pixels and physical world measurements.
    pub fn resolution(&self) -> (f64, f64) {
        let (mut rx, mut ry) = (0.0, 0.0);
        unsafe { (&*self.frame).GetResolution(&mut rx, &mut ry) };
        (rx, ry)
    }

    /// Retrieves the pixel width and height of the bitmap.
    pub fn size(&self) -> (u32, u32) {
        let (mut sx, mut sy) = (0, 0);
        unsafe { (&*self.frame).GetSize(&mut sx, &mut sy) };
        (sx, sy)
    }

    /*  Retrieves the pixel format of the bitmap source. Returns a GUID, Match it to the GUID defined in the link below:

        See https://docs.microsoft.com/en-us/windows/win32/wic/-wic-codec-native-pixel-formats#undefined-pixel-formats
    */
    pub fn pixel_format(&self) -> WICPixelFormatGUID {
        let mut fmt = unsafe { mem::zeroed() };
        unsafe { (&*self.frame).GetPixelFormat(&mut fmt) };
        fmt
    }

    /**
        Copy the frame pixels into a buffer.

        Parameters:
            pixel_size: defines the size of a pixel in bytes. In a typical RGBA image, this would be 4 (1 byte for each component).
                        If unsure, use the pixel_format.

        May return an error if the pixel data could not be read
    */
    pub fn pixels(&self, pixel_size: u32) -> Result<Vec<u8>, NwgError> {
        let (w, h) = self.size();
        let scanline = w * pixel_size;
        let buffer_size = (w * h * pixel_size) as usize;
        let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);

        let hr = unsafe {
            buffer.set_len(buffer_size);
            (&*self.frame).CopyPixels(ptr::null(), scanline, buffer_size as u32, buffer.as_mut_ptr())
        };

        match hr {
            S_OK => Ok(buffer),
            err => Err(NwgError::image_decoder(err, "Could not read image pixels"))
        }
    }

    /**
        Copy a region of the frames pixel into a buffer.

        Parameters:
            offset: The [x,y] offset at which the region begins
            size: The [width, height] size of the region
            pixel_size: defines the size of a pixel in bytes. In a typical RGBA image, this would be 4 (1 byte for each component).
                        If unsure, use the pixel_format.

        May return an error if the pixel data could not be read
    */
    pub fn region_pixels(&self, offset: [i32;2], size: [i32;2], pixel_size: u32) -> Result<Vec<u8>, NwgError> {
        use winapi::um::wincodec::WICRect;

        let [x, y] = offset;
        let [w, h] = size;
        let scanline = (w as u32) * pixel_size;
        let buffer_size = (scanline * (h as u32)) as usize;
        let mut buffer: Vec<u8> = Vec::with_capacity(buffer_size);

        let region = WICRect { X: x, Y: y, Width: w, Height: h };

        let hr = unsafe {
            buffer.set_len(buffer_size);
            (&*self.frame).CopyPixels(&region, scanline, buffer_size as u32, buffer.as_mut_ptr())
        };

        match hr {
            S_OK => Ok(buffer),
            err => Err(NwgError::image_decoder(err, "Could not read image pixels"))
        }
    }

    /**
        Create a bitmap resource the the image data. This resource can then be used in the other NWG component.
        The bitmap returned is considered "owned".
    */
    pub fn as_bitmap(&self) -> Result<Bitmap, NwgError> {
        unsafe { img::create_bitmap_from_wic(self) }
    }

}

/// A list of container format implemented in WIC
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ContainerFormat {
    Unknown,
    Adng,
    Bmp,
    Png,
    Ico,
    Jpeg,
    Tiff,
    Gif,
    Wmp,
}

//
// IMPL
//

impl Default for ImageDecoder {
    fn default() -> ImageDecoder {
        ImageDecoder {
            factory: ptr::null_mut()
        }
    }
}

impl Drop for ImageDecoder {
    fn drop(&mut self) {
        if !self.factory.is_null() {
            unsafe { (&*self.factory).Release(); }
        }
    }
}

impl<'a> Drop for ImageSource<'a> {
    fn drop(&mut self) {
        unsafe { (&*self.decoder).Release(); }
    }
}

impl Drop for ImageData {
    fn drop(&mut self) {
        unsafe { (&*self.frame).Release(); }
    }
}

/**
    A blank builder for the image decoder
*/
pub struct ImageDecoderBuilder {
}

impl ImageDecoderBuilder {
    pub fn build(self, out: &mut ImageDecoder) -> Result<(), NwgError> {
        let factory = unsafe { img::create_image_factory() }?;
        *out = ImageDecoder { factory };
        Ok(())
    }
}
<|endoftext|>


<commit_before>extern crate susurrus;
extern crate getopts;
extern crate sodiumoxide;
extern crate rustc_serialize;

use susurrus::session;
use susurrus::noisebox;
use getopts::Options;
use std::env;
use std::io;
use std::io::Read;
use std::fs::OpenOptions;
use rustc_serialize::hex::{FromHex,ToHex};
use std::string::String;

macro_rules! make_fixed {
    ($t:ty, $n:expr, $v:expr) => ({
        let mut res : [$t; $n] = [0; $n];
        assert!($v.len() == $n);
        for i in 0..$n { res[i] = $v[i] }
        res
    })
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn read_keypair<F:io::Read>(file: &mut F) -> io::Result<session::KeyPair> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-secret:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };
    let mut privkey_raw = [0u8; 64];
    try!(file.read(&mut privkey_raw));
    let privkey = match String::from_utf8_lossy(&privkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::KeyPair { pubkey: session::PubKey(make_fixed!(u8, 32, pubkey)),
                          privkey: session::PrivKey(make_fixed!(u8, 32, privkey)) })
}

fn read_pubkey<F:io::Read>(file: &mut F) -> io::Result<session::PubKey> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-public:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::PubKey(make_fixed!(u8, 32, pubkey)))
}

fn main() {
    sodiumoxide::init();

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("e", "encrypt", "sets the mode to encryption");
    opts.optflag("d", "decrypt", "sets the mode to decryption");
    opts.optflag("k", "keygen", "generate a new key pair");
    opts.optflag("p", "pubkey", "extracts public key from key pair");
    opts.optflag("a", "ascii", "ASCII armor encrypted result");
    opts.optopt("i", "input", "sets input file, defaults to stdin", "NAME");
    opts.optopt("o", "output", "set output file, defaults to stdout", "NAME");
    opts.optopt("r", "remote", "specifies file containing remote's public key", "NAME");
    opts.optopt("l", "local", "specifies file containing local key pair", "NAME");
    opts.optopt("t", "type", "specifies the box type to use (one of n, k, or x)", "TYPE");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let encrypting = match (matches.opt_present("e"), matches.opt_present("d")) {
        (true, true) => {
            println!("Cannot encrypt and decrypt simultaneously");
            return print_usage(&program, opts);
        },
        (true, false) => true,
        (false, true) => false,
        (false, false) => if !matches.opt_present("k") && !matches.opt_present("p") {
            println!("Must specify encryption or decryption");
            return print_usage(&program, opts);
        } else { false }
    };
    let mut output = match matches.opt_str("o") {
        Some(f) => match OpenOptions::new().write(true).create(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Write>,
            Err(e) => return println!("Could not open output file {}: {}", f, e)
        },
        None => Box::new(io::stdout()) as Box<io::Write>
    };
    let mut input = match matches.opt_str("i") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Read>,
            Err(e) => return println!("Could not open input file {}: {}", f, e)
        },
        None => Box::new(io::stdin()) as Box<io::Read>
    };
    let remote = match matches.opt_str("r") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_pubkey(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse remote key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open remote key file {}: {}", f, e)
        },
        None => None
    };
    let local = match matches.opt_str("l") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_keypair(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse local key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open local key file {}: {}", f, e)
        },
        None => None
    };

    if matches.opt_present("k") {
        let kp = session::gen_keypair();
        output.write(&b"noise255-secret:"[..]).unwrap();
        output.write(&kp.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.write(&kp.privkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    if matches.opt_present("p") {
        let local = match local {
            Some(x) => x,
            None => return println!("Key pair must be specified to extract public key")
        };
        output.write(&b"noise255-public:"[..]).unwrap();
        output.write(&local.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    let mut buf = vec![];
    input.read_to_end(&mut buf).unwrap();
    let result = if encrypting {
        let remote = match remote {
            Some(x) => x,
            None => return println!("Must specify remote key when encrypting")
        };
        let res = match local {
            Some(x) => noisebox::BoxX::enbox(x, remote, &buf[..]),
            None => noisebox::BoxN::enbox(remote, &buf[..])
        };
        if matches.opt_present("a") {
            let mut out = String::new();
            out.push_str("noise255-message:");
            out.push_str(&res.to_hex()[..]);
            Ok(out.into_bytes())
        } else {
            Ok(res)
        }
    } else {
        let local = match local {
            Some(x) => x,
            None => return println!("Must specify local key when decrypting")
        };
        let msg = if &buf[..17] == &b"noise255-message:"[..] {
            String::from_utf8_lossy(&buf[..]).from_hex().unwrap()
        } else {
            buf
        };
        match remote {
            Some(x) => noisebox::BoxK::unbox(x, local.clone(), &msg[..]),
            None => Err(())
        }.or(noisebox::BoxN::unbox(local.clone(), &msg[..]))
            .or(noisebox::BoxX::unbox(local, &msg[..]))
    };

    match result {
        Ok(x) => match output.write_all(&x[..]) {
            Ok(()) => (),
            Err(e) => println!("Write failed: {:?}", e)
        },
        Err(e) => println!("Decryption failed: {:?}", e)
    }
    output.flush().unwrap();
}
<commit_msg>Fix ascii armour decryption<commit_after>extern crate susurrus;
extern crate getopts;
extern crate sodiumoxide;
extern crate rustc_serialize;

use susurrus::session;
use susurrus::noisebox;
use getopts::Options;
use std::env;
use std::io;
use std::io::Read;
use std::fs::OpenOptions;
use rustc_serialize::hex::{FromHex,ToHex};
use std::string::String;

macro_rules! make_fixed {
    ($t:ty, $n:expr, $v:expr) => ({
        let mut res : [$t; $n] = [0; $n];
        assert!($v.len() == $n);
        for i in 0..$n { res[i] = $v[i] }
        res
    })
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn read_keypair<F:io::Read>(file: &mut F) -> io::Result<session::KeyPair> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-secret:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };
    let mut privkey_raw = [0u8; 64];
    try!(file.read(&mut privkey_raw));
    let privkey = match String::from_utf8_lossy(&privkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::KeyPair { pubkey: session::PubKey(make_fixed!(u8, 32, pubkey)),
                          privkey: session::PrivKey(make_fixed!(u8, 32, privkey)) })
}

fn read_pubkey<F:io::Read>(file: &mut F) -> io::Result<session::PubKey> {
    let mut magic = [0u8;16];
    try!(file.read(&mut magic));
    if &magic[..] != b"noise255-public:" {
        return Err(io::Error::new(io::ErrorKind::Other, "Missing magic"))
    }

    let mut pubkey_raw = [0u8; 64];
    try!(file.read(&mut pubkey_raw));
    let pubkey = match String::from_utf8_lossy(&pubkey_raw[..]).from_hex() {
        Ok(x) => x,
        Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e))
    };

    Ok(session::PubKey(make_fixed!(u8, 32, pubkey)))
}

fn main() {
    sodiumoxide::init();

    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optflag("e", "encrypt", "sets the mode to encryption");
    opts.optflag("d", "decrypt", "sets the mode to decryption");
    opts.optflag("k", "keygen", "generate a new key pair");
    opts.optflag("p", "pubkey", "extracts public key from key pair");
    opts.optflag("a", "ascii", "ASCII armor encrypted result");
    opts.optopt("i", "input", "sets input file, defaults to stdin", "NAME");
    opts.optopt("o", "output", "set output file, defaults to stdout", "NAME");
    opts.optopt("r", "remote", "specifies file containing remote's public key", "NAME");
    opts.optopt("l", "local", "specifies file containing local key pair", "NAME");
    opts.optopt("t", "type", "specifies the box type to use (one of n, k, or x)", "TYPE");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let encrypting = match (matches.opt_present("e"), matches.opt_present("d")) {
        (true, true) => {
            println!("Cannot encrypt and decrypt simultaneously");
            return print_usage(&program, opts);
        },
        (true, false) => true,
        (false, true) => false,
        (false, false) => if !matches.opt_present("k") && !matches.opt_present("p") {
            println!("Must specify encryption or decryption");
            return print_usage(&program, opts);
        } else { false }
    };
    let mut output = match matches.opt_str("o") {
        Some(f) => match OpenOptions::new().write(true).create(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Write>,
            Err(e) => return println!("Could not open output file {}: {}", f, e)
        },
        None => Box::new(io::stdout()) as Box<io::Write>
    };
    let mut input = match matches.opt_str("i") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(x) => Box::new(x) as Box<io::Read>,
            Err(e) => return println!("Could not open input file {}: {}", f, e)
        },
        None => Box::new(io::stdin()) as Box<io::Read>
    };
    let remote = match matches.opt_str("r") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_pubkey(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse remote key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open remote key file {}: {}", f, e)
        },
        None => None
    };
    let local = match matches.opt_str("l") {
        Some(f) => match OpenOptions::new().read(true).open(&f) {
            Ok(mut x) => match read_keypair(&mut x) {
                Ok(x) => Some(x),
                Err(e) => return println!("Could not parse local key file {}: {}", f, e)
            },
            Err(e) => return println!("Could not open local key file {}: {}", f, e)
        },
        None => None
    };

    if matches.opt_present("k") {
        let kp = session::gen_keypair();
        output.write(&b"noise255-secret:"[..]).unwrap();
        output.write(&kp.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.write(&kp.privkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    if matches.opt_present("p") {
        let local = match local {
            Some(x) => x,
            None => return println!("Key pair must be specified to extract public key")
        };
        output.write(&b"noise255-public:"[..]).unwrap();
        output.write(&local.pubkey.0[..].to_hex().as_bytes()).unwrap();
        output.flush().unwrap();
        return
    }

    let mut buf = vec![];
    input.read_to_end(&mut buf).unwrap();
    let result = if encrypting {
        let remote = match remote {
            Some(x) => x,
            None => return println!("Must specify remote key when encrypting")
        };
        let res = match local {
            Some(x) => noisebox::BoxX::enbox(x, remote, &buf[..]),
            None => noisebox::BoxN::enbox(remote, &buf[..])
        };
        if matches.opt_present("a") {
            let mut out = String::new();
            out.push_str("noise255-message:");
            out.push_str(&res.to_hex()[..]);
            Ok(out.into_bytes())
        } else {
            Ok(res)
        }
    } else {
        let local = match local {
            Some(x) => x,
            None => return println!("Must specify local key when decrypting")
        };
        let msg = if &buf[..17] == &b"noise255-message:"[..] {
            String::from_utf8_lossy(&buf[17..]).from_hex().unwrap()
        } else {
            buf
        };
        match remote {
            Some(x) => noisebox::BoxK::unbox(x, local.clone(), &msg[..]),
            None => Err(())
        }.or(noisebox::BoxN::unbox(local.clone(), &msg[..]))
            .or(noisebox::BoxX::unbox(local, &msg[..]))
    };

    match result {
        Ok(x) => match output.write_all(&x[..]) {
            Ok(()) => (),
            Err(e) => println!("Write failed: {:?}", e)
        },
        Err(e) => println!("Decryption failed: {:?}", e)
    }
    output.flush().unwrap();
}
<|endoftext|>


<commit_before>use nom::{digit, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::collections::{HashSet, VecDeque};
use std::str;

use column::Column;
use common::{binary_comparison_operator, column_identifier, Operator};

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ConditionBase {
    Field(Column),
    Literal(String),
    Placeholder,
}

#[derive(Clone, Debug, Hash, PartialEq)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Box<ConditionExpression>,
    pub right: Box<ConditionExpression>,
}

impl<'a> ConditionTree {
    pub fn contained_columns(&'a self) -> HashSet<&'a Column> {
        let mut s = HashSet::new();
        let mut q = VecDeque::<&'a ConditionTree>::new();
        q.push_back(self);
        while let Some(ref ct) = q.pop_front() {
            match *ct.left.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct) |
                ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
            match *ct.right.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct) |
                ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
        }
        s
    }
}

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    NegationOp(Box<ConditionExpression>),
    Base(ConditionBase),
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               left: and_expr ~
               caseless_tag!("or") ~
               multispace ~
               right: condition_expr,
               || {
                   ConditionExpression::LogicalOp(
                       ConditionTree {
                           operator: Operator::Or,
                           left: Box::new(left),
                           right: Box::new(right),
                       }
                   )
               }
           )
       |   and_expr)
);

named!(pub and_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               left: parenthetical_expr ~
               caseless_tag!("and") ~
               multispace ~
               right: and_expr,
               || {
                   ConditionExpression::LogicalOp(
                       ConditionTree {
                           operator: Operator::And,
                           left: Box::new(left),
                           right: Box::new(right),
                       }
                   )
               }
           )
       |   parenthetical_expr)
);

named!(pub parenthetical_expr<&[u8], ConditionExpression>,
       alt_complete!(
           delimited!(tag!("("), condition_expr, chain!(tag!(")") ~ multispace?, ||{}))
       |   not_expr)
);

named!(pub not_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               caseless_tag!("not") ~
               multispace ~
               right: parenthetical_expr,
               || {
                   ConditionExpression::NegationOp(Box::new(right))
               }
           )
       |   boolean_primary)
);

named!(boolean_primary<&[u8], ConditionExpression>,
    chain!(
        left: predicate ~
        multispace? ~
        op: binary_comparison_operator ~
        multispace? ~
        right: predicate,
        || {
            ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: op,
                    left: Box::new(left),
                    right: Box::new(right),
                }
            )

        }
    )
);

named!(predicate<&[u8], ConditionExpression>,
    alt_complete!(
            chain!(
                delimited!(opt!(multispace), tag!("?"), opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Placeholder
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), digit, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace),
                                  alt_complete!(
                                        delimited!(tag!("\""), opt!(take_until!("\"")), tag!("\""))
                                      | delimited!(tag!("'"), opt!(take_until!("'")), tag!("'"))
                                  ),
                                  opt!(multispace)),
                || {
                    let field = field.unwrap_or("".as_bytes());
                    ConditionExpression::Base(
                        ConditionBase::Literal(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), column_identifier, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Field(field)
                    )
                }
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use common::Operator;

    fn flat_condition_tree(op: Operator,
                           l: ConditionBase,
                           r: ConditionBase)
                           -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
                                              operator: op,
                                              left: Box::new(ConditionExpression::Base(l)),
                                              right: Box::new(ConditionExpression::Base(r)),
                                          })
    }

    #[test]
    fn ct_contained_columns() {
        use std::collections::HashSet;

        let cond = "a.foo = ? and b.bar = 42";

        let res = condition_expr(cond.as_bytes());
        let c1 = Column::from("a.foo");
        let c2 = Column::from("b.bar");
        let mut expected_cols = HashSet::new();
        expected_cols.insert(&c1);
        expected_cols.insert(&c2);
        match res.unwrap().1 {
            ConditionExpression::LogicalOp(ct) => {
                assert_eq!(ct.contained_columns(), expected_cols);
            }
            _ => panic!(),
        }
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Placeholder));
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))));

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("hello"))));
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::GreaterOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("42"))));

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::LessOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from("5"))));
    }

    #[test]
    fn empty_string_literal() {
        let cond = "foo = ''";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Literal(String::from(""))));
    }

    #[test]
    fn parenthesis() {
        let cond = "(foo = ? or bar = 12) and foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let a = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("foo".into()))),
                                 right: Box::new(Base(Placeholder)),
                             });

        let b = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("bar".into()))),
                                 right: Box::new(Base(Literal("12".into()))),
                             });

        let left = LogicalOp(ConditionTree {
                                 operator: Operator::Or,
                                 left: Box::new(a),
                                 right: Box::new(b),
                             });

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(Literal("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::And,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn order_of_operations() {
        let cond = "foo = ? and bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let a = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("foo".into()))),
                                 right: Box::new(Base(Placeholder)),
                             });

        let b = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("bar".into()))),
                                 right: Box::new(Base(Literal("12".into()))),
                             });

        let left = LogicalOp(ConditionTree {
                                 operator: Operator::And,
                                 left: Box::new(a),
                                 right: Box::new(b),
                             });

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(Literal("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::Or,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn negation() {
        let cond = "not bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let left =
            NegationOp(Box::new(ComparisonOp(ConditionTree {
                                                 operator: Operator::Equal,
                                                 left: Box::new(Base(Field("bar".into()))),
                                                 right: Box::new(Base(Literal("12".into()))),
                                             })));

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(Literal("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::Or,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

}
<commit_msg>Support integer literals<commit_after>use nom::{digit, multispace};
use nom::{IResult, Err, ErrorKind, Needed};
use std::collections::{HashSet, VecDeque};
use std::str::{self, FromStr};

use column::Column;
use common::{binary_comparison_operator, column_identifier, Operator};

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ConditionBase {
    Field(Column),
    StringLiteral(String),
    IntegerLiteral(i64),
    Placeholder,
}

#[derive(Clone, Debug, Hash, PartialEq)]
pub struct ConditionTree {
    pub operator: Operator,
    pub left: Box<ConditionExpression>,
    pub right: Box<ConditionExpression>,
}

impl<'a> ConditionTree {
    pub fn contained_columns(&'a self) -> HashSet<&'a Column> {
        let mut s = HashSet::new();
        let mut q = VecDeque::<&'a ConditionTree>::new();
        q.push_back(self);
        while let Some(ref ct) = q.pop_front() {
            match *ct.left.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct) |
                ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
            match *ct.right.as_ref() {
                ConditionExpression::Base(ConditionBase::Field(ref c)) => {
                    s.insert(c);
                }
                ConditionExpression::LogicalOp(ref ct) |
                ConditionExpression::ComparisonOp(ref ct) => q.push_back(ct),
                _ => (),
            }
        }
        s
    }
}

#[derive(Clone, Debug, Hash, PartialEq)]
pub enum ConditionExpression {
    ComparisonOp(ConditionTree),
    LogicalOp(ConditionTree),
    NegationOp(Box<ConditionExpression>),
    Base(ConditionBase),
}

/// Parse a conditional expression into a condition tree structure
named!(pub condition_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               left: and_expr ~
               caseless_tag!("or") ~
               multispace ~
               right: condition_expr,
               || {
                   ConditionExpression::LogicalOp(
                       ConditionTree {
                           operator: Operator::Or,
                           left: Box::new(left),
                           right: Box::new(right),
                       }
                   )
               }
           )
       |   and_expr)
);

named!(pub and_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               left: parenthetical_expr ~
               caseless_tag!("and") ~
               multispace ~
               right: and_expr,
               || {
                   ConditionExpression::LogicalOp(
                       ConditionTree {
                           operator: Operator::And,
                           left: Box::new(left),
                           right: Box::new(right),
                       }
                   )
               }
           )
       |   parenthetical_expr)
);

named!(pub parenthetical_expr<&[u8], ConditionExpression>,
       alt_complete!(
           delimited!(tag!("("), condition_expr, chain!(tag!(")") ~ multispace?, ||{}))
       |   not_expr)
);

named!(pub not_expr<&[u8], ConditionExpression>,
       alt_complete!(
           chain!(
               caseless_tag!("not") ~
               multispace ~
               right: parenthetical_expr,
               || {
                   ConditionExpression::NegationOp(Box::new(right))
               }
           )
       |   boolean_primary)
);

named!(boolean_primary<&[u8], ConditionExpression>,
    chain!(
        left: predicate ~
        multispace? ~
        op: binary_comparison_operator ~
        multispace? ~
        right: predicate,
        || {
            ConditionExpression::ComparisonOp(
                ConditionTree {
                    operator: op,
                    left: Box::new(left),
                    right: Box::new(right),
                }
            )

        }
    )
);

named!(predicate<&[u8], ConditionExpression>,
    alt_complete!(
            chain!(
                delimited!(opt!(multispace), tag!("?"), opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Placeholder
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), digit, opt!(multispace)),
                || {
                    let intval = i64::from_str(str::from_utf8(field).unwrap()).unwrap();
                    ConditionExpression::Base(ConditionBase::IntegerLiteral(intval))
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace),
                                  alt_complete!(
                                        delimited!(tag!("\""), opt!(take_until!("\"")), tag!("\""))
                                      | delimited!(tag!("'"), opt!(take_until!("'")), tag!("'"))
                                  ),
                                  opt!(multispace)),
                || {
                    let field = field.unwrap_or("".as_bytes());
                    ConditionExpression::Base(
                        ConditionBase::StringLiteral(String::from(str::from_utf8(field).unwrap()))
                    )
                }
            )
        |   chain!(
                field: delimited!(opt!(multispace), column_identifier, opt!(multispace)),
                || {
                    ConditionExpression::Base(
                        ConditionBase::Field(field)
                    )
                }
            )
    )
);

#[cfg(test)]
mod tests {
    use super::*;
    use column::Column;
    use common::Operator;

    fn flat_condition_tree(op: Operator,
                           l: ConditionBase,
                           r: ConditionBase)
                           -> ConditionExpression {
        ConditionExpression::ComparisonOp(ConditionTree {
                                              operator: op,
                                              left: Box::new(ConditionExpression::Base(l)),
                                              right: Box::new(ConditionExpression::Base(r)),
                                          })
    }

    #[test]
    fn ct_contained_columns() {
        use std::collections::HashSet;

        let cond = "a.foo = ? and b.bar = 42";

        let res = condition_expr(cond.as_bytes());
        let c1 = Column::from("a.foo");
        let c2 = Column::from("b.bar");
        let mut expected_cols = HashSet::new();
        expected_cols.insert(&c1);
        expected_cols.insert(&c2);
        match res.unwrap().1 {
            ConditionExpression::LogicalOp(ct) => {
                assert_eq!(ct.contained_columns(), expected_cols);
            }
            _ => panic!(),
        }
    }

    #[test]
    fn equality_placeholder() {
        let cond = "foo = ?";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::Placeholder));
    }

    #[test]
    fn equality_literals() {
        let cond1 = "foo = 42";
        let cond2 = "foo = \"hello\"";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::IntegerLiteral(42 as i64)));

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::StringLiteral(String::from("hello"))));
    }

    #[test]
    fn inequality_literals() {
        let cond1 = "foo >= 42";
        let cond2 = "foo <= 5";

        let res1 = condition_expr(cond1.as_bytes());
        assert_eq!(res1.unwrap().1,
                   flat_condition_tree(Operator::GreaterOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::IntegerLiteral(42 as i64)));

        let res2 = condition_expr(cond2.as_bytes());
        assert_eq!(res2.unwrap().1,
                   flat_condition_tree(Operator::LessOrEqual,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::IntegerLiteral(5 as i64)));
    }

    #[test]
    fn empty_string_literal() {
        let cond = "foo = ''";

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1,
                   flat_condition_tree(Operator::Equal,
                                       ConditionBase::Field(Column::from("foo")),
                                       ConditionBase::StringLiteral(String::from(""))));
    }

    #[test]
    fn parenthesis() {
        let cond = "(foo = ? or bar = 12) and foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let a = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("foo".into()))),
                                 right: Box::new(Base(Placeholder)),
                             });

        let b = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("bar".into()))),
                                 right: Box::new(Base(IntegerLiteral(12.into()))),
                             });

        let left = LogicalOp(ConditionTree {
                                 operator: Operator::Or,
                                 left: Box::new(a),
                                 right: Box::new(b),
                             });

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(StringLiteral("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::And,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn order_of_operations() {
        let cond = "foo = ? and bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let a = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("foo".into()))),
                                 right: Box::new(Base(Placeholder)),
                             });

        let b = ComparisonOp(ConditionTree {
                                 operator: Operator::Equal,
                                 left: Box::new(Base(Field("bar".into()))),
                                 right: Box::new(Base(IntegerLiteral(12.into()))),
                             });

        let left = LogicalOp(ConditionTree {
                                 operator: Operator::And,
                                 left: Box::new(a),
                                 right: Box::new(b),
                             });

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(StringLiteral("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::Or,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

    #[test]
    fn negation() {
        let cond = "not bar = 12 or foobar = 'a'";

        use ConditionExpression::*;
        use ConditionBase::*;

        let left =
            NegationOp(Box::new(ComparisonOp(ConditionTree {
                                                 operator: Operator::Equal,
                                                 left: Box::new(Base(Field("bar".into()))),
                                                 right:
                                                     Box::new(Base(IntegerLiteral(12.into()))),
                                             })));

        let right = ComparisonOp(ConditionTree {
                                     operator: Operator::Equal,
                                     left: Box::new(Base(Field("foobar".into()))),
                                     right: Box::new(Base(StringLiteral("a".into()))),
                                 });

        let complete = LogicalOp(ConditionTree {
                                     operator: Operator::Or,
                                     left: Box::new(left),
                                     right: Box::new(right),
                                 });

        let res = condition_expr(cond.as_bytes());
        assert_eq!(res.unwrap().1, complete);
    }

}
<|endoftext|>