<commit_before>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The shell command primitive for running commands on a managed
//! host.
//!
//! # Examples
//!
//! Setup your ZMQ socket, using your managed host's IP address in
//! place of *127.0.0.1*:
//!
//! ```no_run
//! # #[macro_use] extern crate zmq;
//! # fn main() {
//! let mut ctx = zmq::Context::new();
//! let mut zmq_socket = ctx.socket(zmq::REQ).unwrap();
//! zmq_socket.connect("tcp://127.0.0.1:7101").unwrap();
//! # }
//! ```
//!
//! Now run your command and get the result:
//!
//! ```no_run
//! # #[macro_use] extern crate zmq;
//! # #[macro_use] extern crate inapi;
//! # use inapi::Command;
//! # fn main() {
//! # let mut ctx = zmq::Context::new();
//! # let mut zmq_socket = ctx.socket(zmq::REQ).unwrap();
//! let cmd = Command::new("whoami");
//! let result = cmd.exec(&mut zmq_socket).unwrap();
//! println!("Exit: {}, Stdout: {}, Stderr: {}", result.exit_code, result.stdout, result.stderr);
//! # }
//! ```
//!
//! If all goes well, this will output:
//!
//! > Exit: 0, Stdout: root, Stderr:

use ::MissingFrameError;
use std::convert;
use zmq;

/// Reusable container for sending commands to managed hosts.
#[derive(Debug)]
pub struct Command {
    /// The shell command
    pub cmd: String,
}

/// Result attributes returned from the managed host.
pub struct CommandResult {
    /// Exit code for the shell command's process
    pub exit_code: i32,
    /// Process's standard output
    pub stdout: String,
    /// Process's standard error
    pub stderr: String,
}

impl Command {
    /// Create a new Command to represent your shell command.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use inapi::Command;
    /// let cmd = Command::new("your shell command goes here");
    /// ```
    pub fn new(cmd: &str) -> Command {
        Command {
            cmd: String::from(cmd),
        }
    }

    /// Command structs are reusable accross multiple hosts, which is
    /// helpful if you are configuring a group of servers
    /// simultaneously.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # #[macro_use] extern crate zmq;
    /// # #[macro_use] extern crate inapi;
    /// # use inapi::Command;
    /// # fn main() {
    /// # let mut ctx = zmq::Context::new();
    /// # let mut web1_sock = ctx.socket(zmq::REQ).unwrap();
    /// # let mut web2_sock = ctx.socket(zmq::REQ).unwrap();
    /// let cmd = Command::new("whoami");
    /// let result_web1 = cmd.exec(&mut web1_sock).unwrap();
    /// let result_web2 = cmd.exec(&mut web2_sock).unwrap();
    /// # }
    /// ```
    pub fn exec(&self, sock: &mut zmq::Socket) -> Result<CommandResult, CommandError> {
        try!(sock.send_str("command::exec", zmq::SNDMORE));
        try!(sock.send_str(&self.cmd, 0));

        let status = try!(sock.recv_string(0));
        if status.unwrap() == "Err" {
            if sock.get_rcvmore().unwrap() == false {
                return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "err_msg" }));
            }

            return Err(CommandError::AgentError(try!(sock.recv_string(0)).unwrap()));
        }

        let exit_code = try!(sock.recv_msg(0)).as_str().unwrap().parse::<i32>().unwrap();

        if sock.get_rcvmore().unwrap() == false {
            return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "stdout" }));
        }

        let stdout = try!(sock.recv_string(0)).unwrap();

        if sock.get_rcvmore().unwrap() == false {
            return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "stderr" }));
        }

        let stderr = try!(sock.recv_string(0)).unwrap();

        Ok(CommandResult {
            exit_code: exit_code,
            stdout: stdout,
            stderr: stderr,
        })
    }
}

#[derive(Debug)]
pub enum CommandError {
    /// An error string returned from the host's Intecture Agent
    AgentError(String),
    /// Message frames missing in the response from host's Intecture Agent
    FrameError(MissingFrameError),
    /// ZMQ transmission error
    ZmqError(zmq::Error),
}

impl convert::From<zmq::Error> for CommandError {
	fn from(err: zmq::Error) -> CommandError {
		CommandError::ZmqError(err)
	}
}

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

    #[test]
    fn test_exec() {
        let mut ctx = zmq::Context::new();

        let mut agent_sock = ctx.socket(zmq::REP).unwrap();

        let agent_mock = thread::spawn(move || {
            agent_sock.bind("inproc://test_exec").unwrap();

            assert_eq!("command::exec", agent_sock.recv_string(0).unwrap().unwrap());
            assert!(agent_sock.get_rcvmore().unwrap());
            assert_eq!("moo", agent_sock.recv_string(0).unwrap().unwrap());

            agent_sock.send_str("Ok", zmq::SNDMORE).unwrap();
            agent_sock.send_str("0", zmq::SNDMORE).unwrap();
            agent_sock.send_str("cow", zmq::SNDMORE).unwrap();
            agent_sock.send_str("", 0).unwrap();
        });

        let mut req_sock = ctx.socket(zmq::REQ).unwrap();
        req_sock.connect("inproc://test_exec").unwrap();

        let cmd = Command::new("moo");
        cmd.exec(&mut req_sock).unwrap();

        agent_mock.join().unwrap();
    }
}<commit_msg>Added short description for Command::exec<commit_after>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! The shell command primitive for running commands on a managed
//! host.
//!
//! # Examples
//!
//! Setup your ZMQ socket, using your managed host's IP address in
//! place of *127.0.0.1*:
//!
//! ```no_run
//! # #[macro_use] extern crate zmq;
//! # fn main() {
//! let mut ctx = zmq::Context::new();
//! let mut zmq_socket = ctx.socket(zmq::REQ).unwrap();
//! zmq_socket.connect("tcp://127.0.0.1:7101").unwrap();
//! # }
//! ```
//!
//! Now run your command and get the result:
//!
//! ```no_run
//! # #[macro_use] extern crate zmq;
//! # #[macro_use] extern crate inapi;
//! # use inapi::Command;
//! # fn main() {
//! # let mut ctx = zmq::Context::new();
//! # let mut zmq_socket = ctx.socket(zmq::REQ).unwrap();
//! let cmd = Command::new("whoami");
//! let result = cmd.exec(&mut zmq_socket).unwrap();
//! println!("Exit: {}, Stdout: {}, Stderr: {}", result.exit_code, result.stdout, result.stderr);
//! # }
//! ```
//!
//! If all goes well, this will output:
//!
//! > Exit: 0, Stdout: root, Stderr:

use ::MissingFrameError;
use std::convert;
use zmq;

/// Reusable container for sending commands to managed hosts.
#[derive(Debug)]
pub struct Command {
    /// The shell command
    pub cmd: String,
}

/// Result attributes returned from the managed host.
pub struct CommandResult {
    /// Exit code for the shell command's process
    pub exit_code: i32,
    /// Process's standard output
    pub stdout: String,
    /// Process's standard error
    pub stderr: String,
}

impl Command {
    /// Create a new Command to represent your shell command.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use inapi::Command;
    /// let cmd = Command::new("your shell command goes here");
    /// ```
    pub fn new(cmd: &str) -> Command {
        Command {
            cmd: String::from(cmd),
        }
    }

    /// Send request to the Agent to run your shell command.
    ///
    /// Command structs are reusable accross multiple hosts, which is
    /// helpful if you are configuring a group of servers
    /// simultaneously.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # #[macro_use] extern crate zmq;
    /// # #[macro_use] extern crate inapi;
    /// # use inapi::Command;
    /// # fn main() {
    /// # let mut ctx = zmq::Context::new();
    /// # let mut web1_sock = ctx.socket(zmq::REQ).unwrap();
    /// # let mut web2_sock = ctx.socket(zmq::REQ).unwrap();
    /// let cmd = Command::new("whoami");
    /// let result_web1 = cmd.exec(&mut web1_sock).unwrap();
    /// let result_web2 = cmd.exec(&mut web2_sock).unwrap();
    /// # }
    /// ```
    pub fn exec(&self, sock: &mut zmq::Socket) -> Result<CommandResult, CommandError> {
        try!(sock.send_str("command::exec", zmq::SNDMORE));
        try!(sock.send_str(&self.cmd, 0));

        let status = try!(sock.recv_string(0));
        if status.unwrap() == "Err" {
            if sock.get_rcvmore().unwrap() == false {
                return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "err_msg" }));
            }

            return Err(CommandError::AgentError(try!(sock.recv_string(0)).unwrap()));
        }

        let exit_code = try!(sock.recv_msg(0)).as_str().unwrap().parse::<i32>().unwrap();

        if sock.get_rcvmore().unwrap() == false {
            return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "stdout" }));
        }

        let stdout = try!(sock.recv_string(0)).unwrap();

        if sock.get_rcvmore().unwrap() == false {
            return Err(CommandError::FrameError(MissingFrameError { order: 1, name: "stderr" }));
        }

        let stderr = try!(sock.recv_string(0)).unwrap();

        Ok(CommandResult {
            exit_code: exit_code,
            stdout: stdout,
            stderr: stderr,
        })
    }
}

#[derive(Debug)]
pub enum CommandError {
    /// An error string returned from the host's Intecture Agent
    AgentError(String),
    /// Message frames missing in the response from host's Intecture Agent
    FrameError(MissingFrameError),
    /// ZMQ transmission error
    ZmqError(zmq::Error),
}

impl convert::From<zmq::Error> for CommandError {
	fn from(err: zmq::Error) -> CommandError {
		CommandError::ZmqError(err)
	}
}

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

    #[test]
    fn test_exec() {
        let mut ctx = zmq::Context::new();

        let mut agent_sock = ctx.socket(zmq::REP).unwrap();

        let agent_mock = thread::spawn(move || {
            agent_sock.bind("inproc://test_exec").unwrap();

            assert_eq!("command::exec", agent_sock.recv_string(0).unwrap().unwrap());
            assert!(agent_sock.get_rcvmore().unwrap());
            assert_eq!("moo", agent_sock.recv_string(0).unwrap().unwrap());

            agent_sock.send_str("Ok", zmq::SNDMORE).unwrap();
            agent_sock.send_str("0", zmq::SNDMORE).unwrap();
            agent_sock.send_str("cow", zmq::SNDMORE).unwrap();
            agent_sock.send_str("", 0).unwrap();
        });

        let mut req_sock = ctx.socket(zmq::REQ).unwrap();
        req_sock.connect("inproc://test_exec").unwrap();

        let cmd = Command::new("moo");
        cmd.exec(&mut req_sock).unwrap();

        agent_mock.join().unwrap();
    }
}<|endoftext|>


<commit_before>use std::io;
use std::ffi::OsStr;
use std::process::{Child, Command, ExitStatus, Stdio};
use tokenizer::Token;

pub enum CommandResult {
    Grouped,
    Status
}

pub struct ParallelCommand<'a> {
    pub slot_no:          &'a str,
    pub job_no:           &'a str,
    pub job_total:        &'a str,
    pub input:            &'a str,
    pub command_template: &'a [Token],
}

impl<'a> ParallelCommand<'a> {
    pub fn exec(&self, grouped: bool, uses_shell: bool, child: &mut Child)
        -> io::Result<CommandResult>
    {
        // First the arguments will be generated based on the tokens and input.
        let mut arguments = String::with_capacity(self.command_template.len() << 1);
        self.build_arguments(&mut arguments);

        // Check to see if any placeholder tokens are in use.
        let placeholder_exists = self.command_template.iter().any(|x| {
            x == &Token::BaseAndExt || x == &Token::Basename || x == &Token::Dirname ||
            x == &Token::Job || x == &Token::Placeholder || x == &Token::RemoveExtension ||
            x == &Token::Slot
        });

        // If no placeholder tokens are in use, the user probably wants to infer one.
        if !placeholder_exists {
            arguments.push(' ');
            arguments.push_str(self.input);
        }

        if grouped {
            get_command_output(&arguments, uses_shell, child).map(|_| CommandResult::Grouped)
        } else {
            get_command_status(&arguments, uses_shell).map(|_| CommandResult::Status)
        }
    }

    /// Builds arguments using the `tokens` template with the current `input` value.
    /// The arguments will be stored within a `Vec<String>`
    fn build_arguments(&self, arguments: &mut String) {
        for arg in self.command_template {
            match *arg {
                Token::Argument(ref arg) => arguments.push_str(arg),
                Token::Basename        => arguments.push_str(basename(self.input)),
                Token::BaseAndExt      => arguments.push_str(basename(remove_extension(self.input))),
                Token::Dirname         => arguments.push_str(dirname(self.input)),
                Token::Job             => arguments.push_str(self.job_no),
                Token::JobTotal        => arguments.push_str(self.job_total),
                Token::Placeholder     => arguments.push_str(self.input),
                Token::RemoveExtension => arguments.push_str(remove_extension(self.input)),
                Token::Slot            => arguments.push_str(self.slot_no)
            }
        }
    }
}

pub fn get_command_output(command: &str, uses_shell: bool, child: &mut Child)
    -> io::Result<()>
{
    if uses_shell {
        shell_output(command, child)
    } else {
        let arguments = split_into_args(command);
        Command::new(&arguments[0]).args(&arguments[1..])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn().map(|process| {
                *child = process;
                ()
            })
    }
}

pub fn get_command_status(command: &str, uses_shell: bool) -> io::Result<ExitStatus> {
    if uses_shell {
        shell_status(command)
    } else {
        let arguments = split_into_args(command);
        Command::new(&arguments[0]).args(&arguments[1..]).status()
    }
}

#[cfg(windows)]
fn shell_output<S: AsRef<OsStr>>(args: S, child: &mut Child) -> io::Result<()> {
    Command::new("cmd").arg("/C").arg(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn().map(|process| {
            *child = process;
            ()
        })
}

#[cfg(windows)]
fn shell_status<S: AsRef<OsStr>>(args: S) -> io::Result<ExitStatus> {
    Command::new("cmd").arg("/C").arg(args).status()
}

#[cfg(not(windows))]
fn shell_output<S: AsRef<OsStr>>(args: S, child: &mut Child) -> io::Result<()> {
    Command::new("sh").arg("-c").arg(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn().map(|process| {
            *child = process;
            ()
        })
}

#[cfg(not(windows))]
fn shell_status<S: AsRef<OsStr>>(args: S) -> io::Result<ExitStatus> {
    Command::new("sh").arg("-c").arg(args).status()
}

/// Handles quoting of arguments to prevent arguments with spaces as being read as
/// multiple separate arguments. This is only executed when `--no-shell` is in use.
fn split_into_args(command: &str) -> Vec<String> {
    let mut output = Vec::new();
    let mut buffer = String::new();
    let mut quoted = false;
    let mut prev_char_was_a_backslash = false;
    for character in command.chars() {
        if quoted {
            match character {
                '\\' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        prev_char_was_a_backslash = true;
                    }
                },
                '"' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        if !buffer.is_empty() {
                            output.push(buffer.clone());
                            buffer.clear();
                        }
                        quoted = false;
                    }
                },
                _ => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    }
                    buffer.push(character);
                }
            }
        } else {
            match character {
                ' ' => {
                    if prev_char_was_a_backslash {
                        buffer.push(' ');
                        prev_char_was_a_backslash = false;
                    } else {
                        if !buffer.is_empty() {
                            output.push(buffer.clone());
                            buffer.clear();
                        }
                    }
                },
                '\\' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        prev_char_was_a_backslash = true;
                    }
                },
                '"' => {
                    if prev_char_was_a_backslash {
                        buffer.push('"');
                        prev_char_was_a_backslash = false;
                    } else {
                        quoted = true;
                    }
                },
                _ => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        buffer.push(character);
                    }
                }
            }
        }
    }
    output
}

/// Removes the extension of a given input
fn remove_extension(input: &str) -> &str {
    let mut dir_index = 0;
    let mut ext_index = 0;

    for (id, character) in input.chars().enumerate() {
        if character == '/' { dir_index = id }
        if character == '.' { ext_index = id; }
    }

    // Account for hidden files and directories
    if ext_index == 0 || dir_index + 2 > ext_index { input } else { &input[0..ext_index] }
}

fn basename(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { "." } else { &input[0..index] }
}

#[test]
fn path_remove_ext_simple() {
    assert_eq!(remove_extension("foo.txt"), "foo");
}

#[test]
fn path_remove_ext_dir() {
    assert_eq!(remove_extension("dir/foo.txt"), "dir/foo");
}

#[test]
fn path_remove_ext_empty() {
    assert_eq!(remove_extension(""), "");
}

#[test]
fn path_basename_simple() {
    assert_eq!(basename("foo.txt"), "foo.txt");
}

#[test]
fn path_basename_dir() {
    assert_eq!(basename("dir/foo.txt"), "foo.txt");
}

#[test]
fn path_basename_empty() {
    assert_eq!(basename(""), "");
}

#[test]
fn path_dirname_simple() {
    assert_eq!(dirname("foo.txt"), ".");
}

#[test]
fn path_dirname_dir() {
    assert_eq!(dirname("dir/foo.txt"), "dir");
}

#[test]
fn path_dirname_empty() {
    assert_eq!(dirname(""), ".");
}

#[test]
fn build_arguments_test() {
    let tokens = vec![Token::Argument("-i ".to_owned()), Token::Placeholder,
        Token::Argument(" ".to_owned()), Token::RemoveExtension,
        Token::Argument(".mkv".to_owned())];

    let command = ParallelCommand {
        slot_no:   "1",
        job_no:    "1",
        job_total: "1",
        input:     "applesauce.mp4",
        command_template: &tokens,
    };

    let mut arguments = String::new();
    command.build_arguments(&mut arguments);
    assert_eq!(arguments, String::from("-i applesauce.mp4 applesauce.mkv"))
}

#[test]
fn test_split_args() {
    let argument = "ffmpeg -i \"file with spaces\" \"output with spaces\"";
    let expected = vec!["ffmpeg", "-i", "file with spaces", "output with spaces"];
    assert_eq!(split_into_args(argument), expected)
}
<commit_msg>Add additional split_args test and fix discovered bug<commit_after>use std::io;
use std::ffi::OsStr;
use std::process::{Child, Command, ExitStatus, Stdio};
use tokenizer::Token;

pub enum CommandResult {
    Grouped,
    Status
}

pub struct ParallelCommand<'a> {
    pub slot_no:          &'a str,
    pub job_no:           &'a str,
    pub job_total:        &'a str,
    pub input:            &'a str,
    pub command_template: &'a [Token],
}

impl<'a> ParallelCommand<'a> {
    pub fn exec(&self, grouped: bool, uses_shell: bool, child: &mut Child)
        -> io::Result<CommandResult>
    {
        // First the arguments will be generated based on the tokens and input.
        let mut arguments = String::with_capacity(self.command_template.len() << 1);
        self.build_arguments(&mut arguments);

        // Check to see if any placeholder tokens are in use.
        let placeholder_exists = self.command_template.iter().any(|x| {
            x == &Token::BaseAndExt || x == &Token::Basename || x == &Token::Dirname ||
            x == &Token::Job || x == &Token::Placeholder || x == &Token::RemoveExtension ||
            x == &Token::Slot
        });

        // If no placeholder tokens are in use, the user probably wants to infer one.
        if !placeholder_exists {
            arguments.push(' ');
            arguments.push_str(self.input);
        }

        if grouped {
            get_command_output(&arguments, uses_shell, child).map(|_| CommandResult::Grouped)
        } else {
            get_command_status(&arguments, uses_shell).map(|_| CommandResult::Status)
        }
    }

    /// Builds arguments using the `tokens` template with the current `input` value.
    /// The arguments will be stored within a `Vec<String>`
    fn build_arguments(&self, arguments: &mut String) {
        for arg in self.command_template {
            match *arg {
                Token::Argument(ref arg) => arguments.push_str(arg),
                Token::Basename        => arguments.push_str(basename(self.input)),
                Token::BaseAndExt      => arguments.push_str(basename(remove_extension(self.input))),
                Token::Dirname         => arguments.push_str(dirname(self.input)),
                Token::Job             => arguments.push_str(self.job_no),
                Token::JobTotal        => arguments.push_str(self.job_total),
                Token::Placeholder     => arguments.push_str(self.input),
                Token::RemoveExtension => arguments.push_str(remove_extension(self.input)),
                Token::Slot            => arguments.push_str(self.slot_no)
            }
        }
    }
}

pub fn get_command_output(command: &str, uses_shell: bool, child: &mut Child)
    -> io::Result<()>
{
    if uses_shell {
        shell_output(command, child)
    } else {
        let arguments = split_into_args(command);
        Command::new(&arguments[0]).args(&arguments[1..])
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn().map(|process| {
                *child = process;
                ()
            })
    }
}

pub fn get_command_status(command: &str, uses_shell: bool) -> io::Result<ExitStatus> {
    if uses_shell {
        shell_status(command)
    } else {
        let arguments = split_into_args(command);
        Command::new(&arguments[0]).args(&arguments[1..]).status()
    }
}

#[cfg(windows)]
fn shell_output<S: AsRef<OsStr>>(args: S, child: &mut Child) -> io::Result<()> {
    Command::new("cmd").arg("/C").arg(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn().map(|process| {
            *child = process;
            ()
        })
}

#[cfg(windows)]
fn shell_status<S: AsRef<OsStr>>(args: S) -> io::Result<ExitStatus> {
    Command::new("cmd").arg("/C").arg(args).status()
}

#[cfg(not(windows))]
fn shell_output<S: AsRef<OsStr>>(args: S, child: &mut Child) -> io::Result<()> {
    Command::new("sh").arg("-c").arg(args)
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn().map(|process| {
            *child = process;
            ()
        })
}

#[cfg(not(windows))]
fn shell_status<S: AsRef<OsStr>>(args: S) -> io::Result<ExitStatus> {
    Command::new("sh").arg("-c").arg(args).status()
}

/// Handles quoting of arguments to prevent arguments with spaces as being read as
/// multiple separate arguments. This is only executed when `--no-shell` is in use.
fn split_into_args(command: &str) -> Vec<String> {
    let mut output = Vec::new();
    let mut buffer = String::new();
    let mut quoted = false;
    let mut prev_char_was_a_backslash = false;
    for character in command.chars() {
        if quoted {
            match character {
                '\\' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        prev_char_was_a_backslash = true;
                    }
                },
                '"' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        if !buffer.is_empty() {
                            output.push(buffer.clone());
                            buffer.clear();
                        }
                        quoted = false;
                    }
                },
                _ => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    }
                    buffer.push(character);
                }
            }
        } else {
            match character {
                ' ' => {
                    if prev_char_was_a_backslash {
                        buffer.push(' ');
                        prev_char_was_a_backslash = false;
                    } else {
                        if !buffer.is_empty() {
                            output.push(buffer.clone());
                            buffer.clear();
                        }
                    }
                },
                '\\' => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        prev_char_was_a_backslash = true;
                    }
                },
                '"' => {
                    if prev_char_was_a_backslash {
                        buffer.push('"');
                        prev_char_was_a_backslash = false;
                    } else {
                        quoted = true;
                    }
                },
                _ => {
                    if prev_char_was_a_backslash {
                        buffer.push('\\');
                        prev_char_was_a_backslash = false;
                    } else {
                        buffer.push(character);
                    }
                }
            }
        }
    }

    if !buffer.is_empty() { output.push(buffer); }
    output
}

/// Removes the extension of a given input
fn remove_extension(input: &str) -> &str {
    let mut dir_index = 0;
    let mut ext_index = 0;

    for (id, character) in input.chars().enumerate() {
        if character == '/' { dir_index = id }
        if character == '.' { ext_index = id; }
    }

    // Account for hidden files and directories
    if ext_index == 0 || dir_index + 2 > ext_index { input } else { &input[0..ext_index] }
}

fn basename(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { input } else { &input[index+1..] }
}

fn dirname(input: &str) -> &str {
    let mut index = 0;
    for (id, character) in input.chars().enumerate() {
        if character == '/' { index = id; }
    }
    if index == 0 { "." } else { &input[0..index] }
}

#[test]
fn path_remove_ext_simple() {
    assert_eq!(remove_extension("foo.txt"), "foo");
}

#[test]
fn path_remove_ext_dir() {
    assert_eq!(remove_extension("dir/foo.txt"), "dir/foo");
}

#[test]
fn path_remove_ext_empty() {
    assert_eq!(remove_extension(""), "");
}

#[test]
fn path_basename_simple() {
    assert_eq!(basename("foo.txt"), "foo.txt");
}

#[test]
fn path_basename_dir() {
    assert_eq!(basename("dir/foo.txt"), "foo.txt");
}

#[test]
fn path_basename_empty() {
    assert_eq!(basename(""), "");
}

#[test]
fn path_dirname_simple() {
    assert_eq!(dirname("foo.txt"), ".");
}

#[test]
fn path_dirname_dir() {
    assert_eq!(dirname("dir/foo.txt"), "dir");
}

#[test]
fn path_dirname_empty() {
    assert_eq!(dirname(""), ".");
}

#[test]
fn build_arguments_test() {
    let tokens = vec![Token::Argument("-i ".to_owned()), Token::Placeholder,
        Token::Argument(" ".to_owned()), Token::RemoveExtension,
        Token::Argument(".mkv".to_owned())];

    let command = ParallelCommand {
        slot_no:   "1",
        job_no:    "1",
        job_total: "1",
        input:     "applesauce.mp4",
        command_template: &tokens,
    };

    let mut arguments = String::new();
    command.build_arguments(&mut arguments);
    assert_eq!(arguments, String::from("-i applesauce.mp4 applesauce.mkv"))
}

#[test]
fn test_split_args() {
    let argument = "ffmpeg -i \"file with spaces\" \"output with spaces\"";
    let expected = vec!["ffmpeg", "-i", "file with spaces", "output with spaces"];
    assert_eq!(split_into_args(argument), expected);

    let argument = "one\\ two\\\\ three";
    let expected = vec!["one two\\", "three"];
    assert_eq!(split_into_args(argument), expected);
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;

type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn getFileList(&self) -> Option<Vec<FileID>> {
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    fn createEmpty(&self) -> Option<FileID> {
        self.new_file_handle().and_then(|(id, _)| Some(id))
    }

    fn createFile() -> File {
    }

    fn writeFile(f: File) -> BackendOperationResult {
    }

    fn createFileWithContent(content: String) -> BackendOperationResult {
    }

    fn readFile(id: FileID) -> String {
    }

    // TODO: Meta files are not covered yet

    fn new_file_handle(&self) -> Option<(FileID, FSFile)> {
        use uuid::Uuid;
        let uuid = Uuid::new_v4().to_hyphenated_string();
        let pathstr = self.basepath + uuid.as_str();
        let path = Path::new(&pathstr);

        if let Ok(f) = FSFile::create(path) {
            Some((uuid, f))
        } else {
            None
        }
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub dataDump: Option<String>    // Data dump, if any
}

impl StorageBackendError {
    fn new(action: &'static str,
           desc  : &'static str,
           explan: &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            explanation:    String::from(explan),
            dataDump:       data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<commit_msg>Implement: Storage::createFile()<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;

type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn getFileList(&self) -> Option<Vec<FileID>> {
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    fn createEmpty(&self) -> Option<FileID> {
        self.new_file_handle().and_then(|(id, _)| Some(id))
    }

    fn createFile(&self) -> Option<File> {
        self.new_file_handle().and_then(|(id, h)| Some(File::from_handle(id, h)))
    }

    fn writeFile(f: File) -> BackendOperationResult {
    }

    fn createFileWithContent(content: String) -> BackendOperationResult {
    }

    fn readFile(id: FileID) -> String {
    }

    // TODO: Meta files are not covered yet

    fn new_file_handle(&self) -> Option<(FileID, FSFile)> {
        use uuid::Uuid;
        let uuid = Uuid::new_v4().to_hyphenated_string();
        let pathstr = self.basepath + uuid.as_str();
        let path = Path::new(&pathstr);

        if let Ok(f) = FSFile::create(path) {
            Some((uuid, f))
        } else {
            None
        }
    }

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub dataDump: Option<String>    // Data dump, if any
}

impl StorageBackendError {
    fn new(action: &'static str,
           desc  : &'static str,
           explan: &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            explanation:    String::from(explan),
            dataDump:       data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<|endoftext|>


<commit_before>use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use std::collections::{BTreeMap, VecDeque};

use crate::error::RenderError;
use crate::grammar::{HandlebarsParser, Rule};
use pest::iterators::Pair;
use pest::Parser;

pub type Object = BTreeMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[inline]
fn parse_json_visitor_inner<'a>(
    path_stack: &mut VecDeque<&'a str>,
    path: &'a str,
) -> Result<(), RenderError> {
    let parsed_path = HandlebarsParser::parse(Rule::path, path)
        .map(|p| p.flatten())
        .map_err(|_| RenderError::new("Invalid JSON path"))?;

    let mut seg_stack: VecDeque<Pair<Rule>> = VecDeque::new();
    for seg in parsed_path {
        if seg.as_str() == "@root" {
            seg_stack.clear();
            path_stack.clear();
            continue;
        }

        match seg.as_rule() {
            Rule::path_up => {
                path_stack.pop_back();
                if let Some(p) = seg_stack.pop_back() {
                    // also pop array index like [1]
                    if p.as_rule() == Rule::path_raw_id {
                        seg_stack.pop_back();
                    }
                }
            }
            Rule::path_id | Rule::path_raw_id => {
                seg_stack.push_back(seg);
            }
            _ => {}
        }
    }

    for i in seg_stack {
        let span = i.into_span();
        path_stack.push_back(&path[span.start()..span.end()]);
    }
    Ok(())
}

fn parse_json_visitor<'a>(
    path_stack: &mut VecDeque<&'a str>,
    base_path: &'a str,
    path_context: &'a VecDeque<String>,
    relative_path: &'a str,
) -> Result<(), RenderError> {
    let mut parser = HandlebarsParser::parse(Rule::path, relative_path)
        .map(|p| p.flatten())
        .map_err(|_| RenderError::new("Invalid JSON path."))?;

    let mut path_context_depth: i64 = -1;

    while let Some(sg) = parser.next() {
        if sg.as_rule() == Rule::path_up {
            path_context_depth += 1;
        } else {
            break;
        }
    }

    if path_context_depth >= 0 {
        if let Some(context_base_path) = path_context.get(path_context_depth as usize) {
            parse_json_visitor_inner(path_stack, context_base_path)?;
        } else {
            parse_json_visitor_inner(path_stack, base_path)?;
        }
    } else {
        parse_json_visitor_inner(path_stack, base_path)?;
    }

    parse_json_visitor_inner(path_stack, relative_path)?;
    Ok(())
}

pub fn merge_json(base: &Json, addition: &Object) -> Json {
    let mut base_map = match *base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.clone(), v.clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub fn navigate(
        &self,
        base_path: &str,
        path_context: &VecDeque<String>,
        relative_path: &str,
    ) -> Result<Option<&Json>, RenderError> {
        let mut path_stack: VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, base_path, path_context, relative_path)?;

        let paths: Vec<&str> = path_stack.iter().cloned().collect();
        let mut data: Option<&Json> = Some(&self.data);
        for p in &paths {
            if *p == "this" {
                continue;
            }
            data = match data {
                Some(&Json::Array(ref l)) => p
                    .parse::<usize>()
                    .map_err(RenderError::with)
                    .map(|idx_u| l.get(idx_u))?,
                Some(&Json::Object(ref m)) => m.get(*p),
                Some(_) => None,
                None => break,
            }
        }
        Ok(data)
    }

    pub fn data(&self) -> &Json {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

#[cfg(test)]
mod test {
    use crate::context::{self, Context};
    use crate::value::{self, JsonRender};
    use serde_json::value::Map;
    use std::collections::VecDeque;

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "./name/../addr/country")
                .unwrap()
                .unwrap()
                .render(),
            "China".to_string()
        );
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "addr.[country]")
                .unwrap()
                .unwrap()
                .render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            ctx2.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "true".to_string()
        );

        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "titles.[0]")
                .unwrap()
                .unwrap()
                .render(),
            "programmer".to_string()
        );

        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "titles.[0]/../../age")
                .unwrap()
                .unwrap()
                .render(),
            "27".to_string()
        );
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this.titles.[0]/../../age")
                .unwrap()
                .unwrap()
                .render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            ctx1.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "[object]".to_owned()
        );
        assert_eq!(
            ctx2.navigate(".", &VecDeque::new(), "age")
                .unwrap()
                .unwrap()
                .render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let hash = btreemap! {
            "tag".to_owned() => value::to_json("h1")
        };

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            ctx_a1
                .navigate(".", &VecDeque::new(), "age")
                .unwrap()
                .unwrap()
                .render(),
            "4".to_owned()
        );
        assert_eq!(
            ctx_a1
                .navigate(".", &VecDeque::new(), "tag")
                .unwrap()
                .unwrap()
                .render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            ctx_a2
                .navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "[object]".to_owned()
        );
        assert_eq!(
            ctx_a2
                .navigate(".", &VecDeque::new(), "tag")
                .unwrap()
                .unwrap()
                .render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this_name")
                .unwrap()
                .unwrap()
                .render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate("a/b", &VecDeque::new(), "@root/b")
                .unwrap()
                .unwrap()
                .render(),
            "2".to_string()
        );
    }
}
<commit_msg>(fix) clippy fix<commit_after>use serde::Serialize;
use serde_json::value::{to_value, Map, Value as Json};

use std::collections::{BTreeMap, VecDeque};

use crate::error::RenderError;
use crate::grammar::{HandlebarsParser, Rule};
use pest::iterators::Pair;
use pest::Parser;

pub type Object = BTreeMap<String, Json>;

/// The context wrap data you render on your templates.
///
#[derive(Debug, Clone)]
pub struct Context {
    data: Json,
}

#[inline]
fn parse_json_visitor_inner<'a>(
    path_stack: &mut VecDeque<&'a str>,
    path: &'a str,
) -> Result<(), RenderError> {
    let parsed_path = HandlebarsParser::parse(Rule::path, path)
        .map(|p| p.flatten())
        .map_err(|_| RenderError::new("Invalid JSON path"))?;

    let mut seg_stack: VecDeque<Pair<Rule>> = VecDeque::new();
    for seg in parsed_path {
        if seg.as_str() == "@root" {
            seg_stack.clear();
            path_stack.clear();
            continue;
        }

        match seg.as_rule() {
            Rule::path_up => {
                path_stack.pop_back();
                if let Some(p) = seg_stack.pop_back() {
                    // also pop array index like [1]
                    if p.as_rule() == Rule::path_raw_id {
                        seg_stack.pop_back();
                    }
                }
            }
            Rule::path_id | Rule::path_raw_id => {
                seg_stack.push_back(seg);
            }
            _ => {}
        }
    }

    for i in seg_stack {
        let span = i.into_span();
        path_stack.push_back(&path[span.start()..span.end()]);
    }
    Ok(())
}

fn parse_json_visitor<'a>(
    path_stack: &mut VecDeque<&'a str>,
    base_path: &'a str,
    path_context: &'a VecDeque<String>,
    relative_path: &'a str,
) -> Result<(), RenderError> {
    let parser = HandlebarsParser::parse(Rule::path, relative_path)
        .map(|p| p.flatten())
        .map_err(|_| RenderError::new("Invalid JSON path."))?;

    let mut path_context_depth: i64 = -1;

    for sg in parser {
        if sg.as_rule() == Rule::path_up {
            path_context_depth += 1;
        } else {
            break;
        }
    }

    if path_context_depth >= 0 {
        if let Some(context_base_path) = path_context.get(path_context_depth as usize) {
            parse_json_visitor_inner(path_stack, context_base_path)?;
        } else {
            parse_json_visitor_inner(path_stack, base_path)?;
        }
    } else {
        parse_json_visitor_inner(path_stack, base_path)?;
    }

    parse_json_visitor_inner(path_stack, relative_path)?;
    Ok(())
}

pub fn merge_json(base: &Json, addition: &Object) -> Json {
    let mut base_map = match *base {
        Json::Object(ref m) => m.clone(),
        _ => Map::new(),
    };

    for (k, v) in addition.iter() {
        base_map.insert(k.clone(), v.clone());
    }

    Json::Object(base_map)
}

impl Context {
    /// Create a context with null data
    pub fn null() -> Context {
        Context { data: Json::Null }
    }

    /// Create a context with given data
    pub fn wraps<T: Serialize>(e: T) -> Result<Context, RenderError> {
        to_value(e)
            .map_err(RenderError::from)
            .map(|d| Context { data: d })
    }

    /// Navigate the context with base path and relative path
    /// Typically you will set base path to `RenderContext.get_path()`
    /// and set relative path to helper argument or so.
    ///
    /// If you want to navigate from top level, set the base path to `"."`
    pub fn navigate(
        &self,
        base_path: &str,
        path_context: &VecDeque<String>,
        relative_path: &str,
    ) -> Result<Option<&Json>, RenderError> {
        let mut path_stack: VecDeque<&str> = VecDeque::new();
        parse_json_visitor(&mut path_stack, base_path, path_context, relative_path)?;

        let paths: Vec<&str> = path_stack.iter().cloned().collect();
        let mut data: Option<&Json> = Some(&self.data);
        for p in &paths {
            if *p == "this" {
                continue;
            }
            data = match data {
                Some(&Json::Array(ref l)) => p
                    .parse::<usize>()
                    .map_err(RenderError::with)
                    .map(|idx_u| l.get(idx_u))?,
                Some(&Json::Object(ref m)) => m.get(*p),
                Some(_) => None,
                None => break,
            }
        }
        Ok(data)
    }

    pub fn data(&self) -> &Json {
        &self.data
    }

    pub fn data_mut(&mut self) -> &mut Json {
        &mut self.data
    }
}

#[cfg(test)]
mod test {
    use crate::context::{self, Context};
    use crate::value::{self, JsonRender};
    use serde_json::value::Map;
    use std::collections::VecDeque;

    #[derive(Serialize)]
    struct Address {
        city: String,
        country: String,
    }

    #[derive(Serialize)]
    struct Person {
        name: String,
        age: i16,
        addr: Address,
        titles: Vec<String>,
    }

    #[test]
    fn test_render() {
        let v = "hello";
        let ctx = Context::wraps(&v.to_string()).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            v.to_string()
        );
    }

    #[test]
    fn test_navigation() {
        let addr = Address {
            city: "Beijing".to_string(),
            country: "China".to_string(),
        };

        let person = Person {
            name: "Ning Sun".to_string(),
            age: 27,
            addr,
            titles: vec!["programmer".to_string(), "cartographier".to_string()],
        };

        let ctx = Context::wraps(&person).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "./name/../addr/country")
                .unwrap()
                .unwrap()
                .render(),
            "China".to_string()
        );
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "addr.[country]")
                .unwrap()
                .unwrap()
                .render(),
            "China".to_string()
        );

        let v = true;
        let ctx2 = Context::wraps(&v).unwrap();
        assert_eq!(
            ctx2.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "true".to_string()
        );

        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "titles.[0]")
                .unwrap()
                .unwrap()
                .render(),
            "programmer".to_string()
        );

        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "titles.[0]/../../age")
                .unwrap()
                .unwrap()
                .render(),
            "27".to_string()
        );
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this.titles.[0]/../../age")
                .unwrap()
                .unwrap()
                .render(),
            "27".to_string()
        );
    }

    #[test]
    fn test_this() {
        let mut map_with_this = Map::new();
        map_with_this.insert("this".to_string(), value::to_json("hello"));
        map_with_this.insert("age".to_string(), value::to_json(5usize));
        let ctx1 = Context::wraps(&map_with_this).unwrap();

        let mut map_without_this = Map::new();
        map_without_this.insert("age".to_string(), value::to_json(4usize));
        let ctx2 = Context::wraps(&map_without_this).unwrap();

        assert_eq!(
            ctx1.navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "[object]".to_owned()
        );
        assert_eq!(
            ctx2.navigate(".", &VecDeque::new(), "age")
                .unwrap()
                .unwrap()
                .render(),
            "4".to_owned()
        );
    }

    #[test]
    fn test_merge_json() {
        let map = json!({ "age": 4 });
        let s = "hello".to_owned();
        let hash = btreemap! {
            "tag".to_owned() => value::to_json("h1")
        };

        let ctx_a1 = Context::wraps(&context::merge_json(&map, &hash)).unwrap();
        assert_eq!(
            ctx_a1
                .navigate(".", &VecDeque::new(), "age")
                .unwrap()
                .unwrap()
                .render(),
            "4".to_owned()
        );
        assert_eq!(
            ctx_a1
                .navigate(".", &VecDeque::new(), "tag")
                .unwrap()
                .unwrap()
                .render(),
            "h1".to_owned()
        );

        let ctx_a2 = Context::wraps(&context::merge_json(&value::to_json(s), &hash)).unwrap();
        assert_eq!(
            ctx_a2
                .navigate(".", &VecDeque::new(), "this")
                .unwrap()
                .unwrap()
                .render(),
            "[object]".to_owned()
        );
        assert_eq!(
            ctx_a2
                .navigate(".", &VecDeque::new(), "tag")
                .unwrap()
                .unwrap()
                .render(),
            "h1".to_owned()
        );
    }

    #[test]
    fn test_key_name_with_this() {
        let m = btreemap! {
            "this_name".to_string() => "the_value".to_string()
        };
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate(".", &VecDeque::new(), "this_name")
                .unwrap()
                .unwrap()
                .render(),
            "the_value".to_string()
        );
    }

    use serde::ser::Error as SerdeError;
    use serde::{Serialize, Serializer};

    struct UnserializableType {}

    impl Serialize for UnserializableType {
        fn serialize<S>(&self, _: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            Err(SerdeError::custom("test"))
        }
    }

    #[test]
    fn test_serialize_error() {
        let d = UnserializableType {};
        assert!(Context::wraps(&d).is_err());
    }

    #[test]
    fn test_root() {
        let m = json!({
            "a" : {
                "b" : {
                    "c" : {
                        "d" : 1
                    }
                }
            },
            "b": 2
        });
        let ctx = Context::wraps(&m).unwrap();
        assert_eq!(
            ctx.navigate("a/b", &VecDeque::new(), "@root/b")
                .unwrap()
                .unwrap()
                .render(),
            "2".to_string()
        );
    }
}
<|endoftext|>


<commit_before>const MONTHS_TO_YEAR: i32 = 12;
const MICROS_TO_HOUR: i64 = 3600000000;
const MICROS_TO_MINUTE: i64 = 60000000;
const MICROS_TO_SECOND: i64 = 1000000;

pub fn get_year_month_interval(months: i32) -> (i32, i8) {
    let years: i32 = months/MONTHS_TO_YEAR;
    let months: i8 = (months - (years * MONTHS_TO_YEAR)) as i8;
    (years, months)
}

pub fn get_day_time_interval(microseconds: i64) -> (i64, i8, f64) {
    let hours: i64 = microseconds/MICROS_TO_HOUR;
    let current_hours: i64 = hours * MICROS_TO_HOUR;
    let minutes: i64 = (microseconds - current_hours)/MICROS_TO_MINUTE;
    let seconds: f64 = (microseconds - (current_hours+(minutes * MICROS_TO_MINUTE))) as f64/MICROS_TO_SECOND as f64;
    (hours, minutes as i8, seconds)
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn negative_year_month_interval() {
        let months = i32::min_value();
        let (years, months) = get_year_month_interval(months);
        assert!(years == -178956970);
        assert!(months == -8);
    }

    #[test]
    fn positive_year_month_interval() {
        let months = i32::max_value();
        let (years, months) = get_year_month_interval(months);
        assert!(years == 178956970);
        assert!(months == 7);
    }

    #[test]
    fn negative_day_time_interval() {
        let microseconds = -7199000000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == -1);
        assert!(minutes == -59);
        assert!(seconds == -59.0);
    }

    #[test]
    fn postive_day_time_interval() {
        let microseconds = 7199000000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.0);
    }

    #[test]
    fn day_time_remaining_milliseconds() {
        let microseconds = 7199001000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.001);
    }

    #[test]
    fn day_time_remaining_millisecond() {
        let microseconds = 7199000001;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.000001);
    }
}
<commit_msg>feat(helpers): add helper functions to help parse float numbers and to get absolute numbers<commit_after>const MONTHS_TO_YEAR: i32 = 12;
const MICROS_TO_HOUR: i64 = 3600000000;
const MICROS_TO_MINUTE: i64 = 60000000;
const MICROS_TO_SECOND: i64 = 1000000;

pub fn get_year_month_interval(months: i32) -> (i32, i8) {
    let years: i32 = months/MONTHS_TO_YEAR;
    let months: i8 = (months - (years * MONTHS_TO_YEAR)) as i8;
    (years, months)
}

pub fn get_day_time_interval(microseconds: i64) -> (i64, i8, f64) {
    let hours: i64 = microseconds/MICROS_TO_HOUR;
    let current_hours: i64 = hours * MICROS_TO_HOUR;
    let minutes: i64 = (microseconds - current_hours)/MICROS_TO_MINUTE;
    let seconds: f64 = (microseconds - (current_hours+(minutes * MICROS_TO_MINUTE))) as f64/MICROS_TO_SECOND as f64;
    (hours, minutes as i8, seconds)
}

pub fn split_seconds(seconds: f64) -> (i16, String) {
    let seconds = seconds.to_string();
    let second_stamp: Vec<&str> = seconds.split(".").collect();
    let mut whole_seconds: i16 = second_stamp[0].parse().unwrap();
    whole_seconds = whole_seconds.abs();
    if let Some(remainder) = second_stamp.get(1) {
        (whole_seconds, (*remainder).to_owned())
    } else {
        (whole_seconds, "0".to_owned())
    }
}

pub fn get_absolute(number: i8) -> i16 {
    let mut number = number as i16;
    number = number.abs();
    number
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn negative_year_month_interval() {
        let months = i32::min_value();
        let (years, months) = get_year_month_interval(months);
        assert!(years == -178956970);
        assert!(months == -8);
    }

    #[test]
    fn positive_year_month_interval() {
        let months = i32::max_value();
        let (years, months) = get_year_month_interval(months);
        assert!(years == 178956970);
        assert!(months == 7);
    }

    #[test]
    fn negative_day_time_interval() {
        let microseconds = -7199000000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == -1);
        assert!(minutes == -59);
        assert!(seconds == -59.0);
    }

    #[test]
    fn postive_day_time_interval() {
        let microseconds = 7199000000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.0);
    }

    #[test]
    fn day_time_remaining_milliseconds() {
        let microseconds = 7199001000;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.001);
    }

    #[test]
    fn day_time_remaining_millisecond() {
        let microseconds = 7199000001;
        let (hours, minutes, seconds) = get_day_time_interval(microseconds);
        assert!(hours == 1);
        assert!(minutes == 59);
        assert!(seconds == 59.000001);
    }
}
<|endoftext|>


<commit_before>#![deny(
    absolute_paths_not_starting_with_crate,
    ambiguous_associated_items,
    anonymous_parameters,
    const_err,
    dead_code,
    deprecated,
    deprecated_in_future,
    duplicate_macro_exports,
    ellipsis_inclusive_range_patterns,
    exceeding_bitshifts,
    explicit_outlives_requirements,
    exported_private_dependencies,
    ill_formed_attribute_input,
    illegal_floating_point_literal_pattern,
    improper_ctypes,
    indirect_structural_match,
    invalid_type_param_default,
    irrefutable_let_patterns,
    keyword_idents,
    late_bound_lifetime_arguments,
    legacy_constructor_visibility,
    legacy_directory_ownership,
    macro_expanded_macro_exports_accessed_by_absolute_paths,
    missing_copy_implementations,
    missing_docs,
    missing_fragment_specifier,
    mutable_borrow_reservation_conflict,
    mutable_transmutes,
    nested_impl_trait,
    no_mangle_const_items,
    no_mangle_generic_items,
    non_ascii_idents,
    non_camel_case_types,
    non_shorthand_field_patterns,
    non_snake_case,
    non_upper_case_globals,
    order_dependent_trait_objects,
    overflowing_literals,
    parenthesized_params_in_types_and_modules,
    path_statements,
    patterns_in_fns_without_body,
    plugin_as_library,
    private_doc_tests,
    private_in_public,
    proc_macro_derive_resolution_fallback,
    pub_use_of_private_extern_crate,
    safe_extern_statics,
    safe_packed_borrows,
    stable_features,
    trivial_bounds,
    trivial_casts,
    trivial_numeric_casts,
    type_alias_bounds,
    tyvar_behind_raw_pointer,
    unconditional_recursion,
    unions_with_drop_fields,
    unknown_crate_types,
    unnameable_test_items,
    unreachable_code,
    unreachable_patterns,
    unreachable_pub,
    unsafe_code,
    unstable_features,
    unstable_name_collisions,
    unused_allocation,
    unused_assignments,
    unused_attributes,
    unused_comparisons,
    unused_doc_comments,
    unused_extern_crates,
    unused_features,
    unused_import_braces,
    unused_imports,
    unused_labels,
    unused_lifetimes,
    unused_macros,
    unused_must_use,
    unused_mut,
    unused_parens,
    unused_qualifications,
    unused_unsafe,
    unused_variables,
    where_clauses_object_safety,
    while_true
)]
#![warn(macro_use_extern_crate, unknown_lints)]
#![allow(
    bare_trait_objects,
    box_pointers,
    elided_lifetimes_in_paths,
    intra_doc_link_resolution_failure,
    missing_doc_code_examples,
    missing_debug_implementations,
    single_use_lifetimes,
    unused_results,
    variant_size_differences,
    warnings,
    renamed_and_removed_lints
)]
#![cfg_attr(feature = "clippy", feature(plugin))]

//! # cargo-make
//!
//! Rust task runner and build tool.

//! The cargo-make task runner enables to define and configure sets of tasks and run them as a flow.

//! A task is a command or a script to execute.

//! Tasks can have dependencies which are also tasks that will be executed before the task itself.

//! With a simple toml based configuration file, you can define a multi platform build script that can run build, test,
//! documentation generation, bench tests execution, security validations and more by running a single command.
//!
//! ## Installation
//! In order to install, just run the following command
//!
//! ```sh
//! cargo install cargo-make
//! ```
//!
//! This will install cargo-make in your ~/.cargo/bin.

//! Make sure to add ~/.cargo/bin directory to your PATH variable.
//!
//! # Contributing
//! See [contributing guide](https://github.com/sagiegurari/cargo-make/blob/master/.github/CONTRIBUTING.md)
//!
//! # License
//! Developed by Sagie Gur-Ari and licensed under the
//! [Apache 2](https://github.com/sagiegurari/cargo-make/blob/master/LICENSE) open source license.
//!

#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;

#[cfg(test)]
mod test;

#[cfg(test)]
#[path = "./mod_test.rs"]
mod mod_test;

// make types public for docs
pub mod types;

mod cache;
mod cli;
mod cli_commands;
mod command;
mod condition;
mod config;
mod descriptor;
mod environment;
mod execution_plan;
mod functions;
mod installer;
mod io;
mod legacy;
mod logger;
mod profile;
mod runner;
mod scriptengine;
mod storage;
mod toolchain;
mod version;

/// Handles the command line arguments and executes the runner.
pub fn run_cli(command_name: String, sub_command: bool) {
    cli::run_cli(command_name, sub_command)
}
<commit_msg>remove clippy plugin<commit_after>#![deny(
    absolute_paths_not_starting_with_crate,
    ambiguous_associated_items,
    anonymous_parameters,
    const_err,
    dead_code,
    deprecated,
    deprecated_in_future,
    duplicate_macro_exports,
    ellipsis_inclusive_range_patterns,
    exceeding_bitshifts,
    explicit_outlives_requirements,
    exported_private_dependencies,
    ill_formed_attribute_input,
    illegal_floating_point_literal_pattern,
    improper_ctypes,
    indirect_structural_match,
    invalid_type_param_default,
    irrefutable_let_patterns,
    keyword_idents,
    late_bound_lifetime_arguments,
    legacy_constructor_visibility,
    legacy_directory_ownership,
    macro_expanded_macro_exports_accessed_by_absolute_paths,
    missing_copy_implementations,
    missing_docs,
    missing_fragment_specifier,
    mutable_borrow_reservation_conflict,
    mutable_transmutes,
    nested_impl_trait,
    no_mangle_const_items,
    no_mangle_generic_items,
    non_ascii_idents,
    non_camel_case_types,
    non_shorthand_field_patterns,
    non_snake_case,
    non_upper_case_globals,
    order_dependent_trait_objects,
    overflowing_literals,
    parenthesized_params_in_types_and_modules,
    path_statements,
    patterns_in_fns_without_body,
    plugin_as_library,
    private_doc_tests,
    private_in_public,
    proc_macro_derive_resolution_fallback,
    pub_use_of_private_extern_crate,
    safe_extern_statics,
    safe_packed_borrows,
    stable_features,
    trivial_bounds,
    trivial_casts,
    trivial_numeric_casts,
    type_alias_bounds,
    tyvar_behind_raw_pointer,
    unconditional_recursion,
    unions_with_drop_fields,
    unknown_crate_types,
    unnameable_test_items,
    unreachable_code,
    unreachable_patterns,
    unreachable_pub,
    unsafe_code,
    unstable_features,
    unstable_name_collisions,
    unused_allocation,
    unused_assignments,
    unused_attributes,
    unused_comparisons,
    unused_doc_comments,
    unused_extern_crates,
    unused_features,
    unused_import_braces,
    unused_imports,
    unused_labels,
    unused_lifetimes,
    unused_macros,
    unused_must_use,
    unused_mut,
    unused_parens,
    unused_qualifications,
    unused_unsafe,
    unused_variables,
    where_clauses_object_safety,
    while_true
)]
#![warn(macro_use_extern_crate, unknown_lints)]
#![allow(
    bare_trait_objects,
    box_pointers,
    elided_lifetimes_in_paths,
    intra_doc_link_resolution_failure,
    missing_doc_code_examples,
    missing_debug_implementations,
    single_use_lifetimes,
    unused_results,
    variant_size_differences,
    warnings,
    renamed_and_removed_lints
)]

//! # cargo-make
//!
//! Rust task runner and build tool.

//! The cargo-make task runner enables to define and configure sets of tasks and run them as a flow.

//! A task is a command or a script to execute.

//! Tasks can have dependencies which are also tasks that will be executed before the task itself.

//! With a simple toml based configuration file, you can define a multi platform build script that can run build, test,
//! documentation generation, bench tests execution, security validations and more by running a single command.
//!
//! ## Installation
//! In order to install, just run the following command
//!
//! ```sh
//! cargo install cargo-make
//! ```
//!
//! This will install cargo-make in your ~/.cargo/bin.

//! Make sure to add ~/.cargo/bin directory to your PATH variable.
//!
//! # Contributing
//! See [contributing guide](https://github.com/sagiegurari/cargo-make/blob/master/.github/CONTRIBUTING.md)
//!
//! # License
//! Developed by Sagie Gur-Ari and licensed under the
//! [Apache 2](https://github.com/sagiegurari/cargo-make/blob/master/LICENSE) open source license.
//!

#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;

#[cfg(test)]
mod test;

#[cfg(test)]
#[path = "./mod_test.rs"]
mod mod_test;

// make types public for docs
pub mod types;

mod cache;
mod cli;
mod cli_commands;
mod command;
mod condition;
mod config;
mod descriptor;
mod environment;
mod execution_plan;
mod functions;
mod installer;
mod io;
mod legacy;
mod logger;
mod profile;
mod runner;
mod scriptengine;
mod storage;
mod toolchain;
mod version;

/// Handles the command line arguments and executes the runner.
pub fn run_cli(command_name: String, sub_command: bool) {
    cli::run_cli(command_name, sub_command)
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::mem;

use runtime::{Class, Object, Sel, Super, self};

/// Types that may be sent Objective-C messages.
/// For example: objects, classes, and blocks.
pub unsafe trait Message { }

unsafe impl Message for Object { }

unsafe impl Message for Class { }

#[cfg(target_arch = "x86")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // Structures 1 or 2 bytes in size are placed in EAX.
    // Structures 4 or 8 bytes in size are placed in: EAX and EDX.
    // Structures of other sizes are placed at the address supplied by the caller.
    // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html

    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    let size = mem::size_of::<R>();
    if type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSend_fpret) }
    } else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "x86")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    let size = mem::size_of::<R>();
    if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "x86_64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // If the size of an object is larger than two eightbytes, it has class MEMORY.
    // If the type has class MEMORY, then the caller provides space for the return
    // value and passes the address of this storage.
    // http://people.freebsd.org/~obrien/amd64-elf-abi.pdf

    if mem::size_of::<R>() <= 16 {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "x86_64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
    if mem::size_of::<R>() <= 16 {
        unsafe { mem::transmute(runtime::objc_msgSendSuper) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
    }
}

#[cfg(target_arch = "arm")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // Double-word sized fundamental data types don't use stret,
    // but any composite type larger than 4 bytes does.
    // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf

    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    if mem::size_of::<R>() <= 4 ||
            type_id == TypeId::of::<i64>() ||
            type_id == TypeId::of::<u64>() ||
            type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "arm")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    if mem::size_of::<R>() <= 4 ||
            type_id == TypeId::of::<i64>() ||
            type_id == TypeId::of::<u64>() ||
            type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSendSuper) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
    }
}

#[cfg(target_arch = "aarch64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // stret is not even available in arm64.
    // https://twitter.com/gparker/status/378079715824660480

    unsafe { mem::transmute(runtime::objc_msgSend) }
}

#[cfg(target_arch = "aarch64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
    unsafe { mem::transmute(runtime::objc_msgSendSuper) }
}

/// Types that may be used as the arguments of an Objective-C message.
pub trait MessageArguments {
    /// Sends a message to the given obj with the given selector and self as
    /// the arguments.
    ///
    /// The correct version of `objc_msgSend` will be chosen based on the
    /// return type. For more information, see Apple's documenation:
    /// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418-CH1g-88778
    ///
    /// It is recommended to use the `msg_send!` macro rather than calling this
    /// method directly.
    unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R
            where T: Message, R: Any;

    /// Sends a message to the superclass of an instance of a class with self
    /// as the arguments.
    unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
            where T: Message, R: Any;
}

macro_rules! message_args_impl {
    ($($a:ident : $t:ident),*) => (
        impl<$($t),*> MessageArguments for ($($t,)*) {
            unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R
                    where T: Message, R: Any {
                let msg_send_fn = msg_send_fn::<R>();
                let msg_send_fn: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R =
                    mem::transmute(msg_send_fn);
                let ($($a,)*) = self;
                objc_try!({
                    msg_send_fn(obj as *mut Object, sel $(, $a)*)
                })
            }

            unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
                    where T: Message, R: Any {
                let msg_send_fn = msg_send_super_fn::<R>();
                let msg_send_fn: unsafe extern fn(*const Super, Sel $(, $t)*) -> R =
                    mem::transmute(msg_send_fn);
                let sup = Super { receiver: obj as *mut Object, superclass: superclass };
                let ($($a,)*) = self;
                objc_try!({
                    msg_send_fn(&sup, sel $(, $a)*)
                })
            }
        }
    );
}

message_args_impl!();
message_args_impl!(a: A);
message_args_impl!(a: A, b: B);
message_args_impl!(a: A, b: B, c: C);
message_args_impl!(a: A, b: B, c: C, d: D);
message_args_impl!(a: A, b: B, c: C, d: D, e: E);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

#[cfg(test)]
mod tests {
    use runtime::Object;
    use test_utils;

    #[test]
    fn test_send_message() {
        let obj = test_utils::sample_object();
        let result: *const Object = unsafe {
            msg_send![obj, self]
        };
        assert!(result == &*obj);
    }

    #[test]
    fn test_send_message_stret() {
        let obj = test_utils::custom_object();
        let result: test_utils::CustomStruct = unsafe {
            msg_send![obj, customStruct]
        };
        let expected = test_utils::CustomStruct { a: 1, b:2, c: 3, d: 4 };
        assert!(result == expected);
    }

    #[test]
    fn test_send_message_nil() {
        let nil: *mut Object = ::std::ptr::null_mut();
        let result: usize = unsafe {
            msg_send![nil, hash]
        };
        assert!(result == 0);

        let result: *mut Object = unsafe {
            msg_send![nil, description]
        };
        assert!(result.is_null());

        let result: f64 = unsafe {
            msg_send![nil, doubleValue]
        };
        assert!(result == 0.0);
    }

    #[test]
    fn test_send_message_super() {
        let obj = test_utils::custom_subclass_object();
        let superclass = test_utils::custom_class();
        unsafe {
            let _: () = msg_send![obj, setFoo:4u32];
            let foo: u32 = msg_send![super(obj, superclass), foo];
            assert!(foo == 4);

            // The subclass is overriden to return foo + 2
            let foo: u32 = msg_send![obj, foo];
            assert!(foo == 6);
        }
    }
}
<commit_msg>Fixed typo in x86 messaging super.<commit_after>use std::any::Any;
use std::mem;

use runtime::{Class, Object, Sel, Super, self};

/// Types that may be sent Objective-C messages.
/// For example: objects, classes, and blocks.
pub unsafe trait Message { }

unsafe impl Message for Object { }

unsafe impl Message for Class { }

#[cfg(target_arch = "x86")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // Structures 1 or 2 bytes in size are placed in EAX.
    // Structures 4 or 8 bytes in size are placed in: EAX and EDX.
    // Structures of other sizes are placed at the address supplied by the caller.
    // https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html

    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    let size = mem::size_of::<R>();
    if type_id == TypeId::of::<f32>() || type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSend_fpret) }
    } else if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "x86")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    let size = mem::size_of::<R>();
    if size == 0 || size == 1 || size == 2 || size == 4 || size == 8 {
        unsafe { mem::transmute(runtime::objc_msgSendSuper) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
    }
}

#[cfg(target_arch = "x86_64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // If the size of an object is larger than two eightbytes, it has class MEMORY.
    // If the type has class MEMORY, then the caller provides space for the return
    // value and passes the address of this storage.
    // http://people.freebsd.org/~obrien/amd64-elf-abi.pdf

    if mem::size_of::<R>() <= 16 {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "x86_64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
    if mem::size_of::<R>() <= 16 {
        unsafe { mem::transmute(runtime::objc_msgSendSuper) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
    }
}

#[cfg(target_arch = "arm")]
fn msg_send_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // Double-word sized fundamental data types don't use stret,
    // but any composite type larger than 4 bytes does.
    // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf

    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    if mem::size_of::<R>() <= 4 ||
            type_id == TypeId::of::<i64>() ||
            type_id == TypeId::of::<u64>() ||
            type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSend) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSend_stret) }
    }
}

#[cfg(target_arch = "arm")]
fn msg_send_super_fn<R: Any>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    use std::any::TypeId;

    let type_id = TypeId::of::<R>();
    if mem::size_of::<R>() <= 4 ||
            type_id == TypeId::of::<i64>() ||
            type_id == TypeId::of::<u64>() ||
            type_id == TypeId::of::<f64>() {
        unsafe { mem::transmute(runtime::objc_msgSendSuper) }
    } else {
        unsafe { mem::transmute(runtime::objc_msgSendSuper_stret) }
    }
}

#[cfg(target_arch = "aarch64")]
fn msg_send_fn<R>() -> unsafe extern fn(*mut Object, Sel, ...) -> R {
    // stret is not even available in arm64.
    // https://twitter.com/gparker/status/378079715824660480

    unsafe { mem::transmute(runtime::objc_msgSend) }
}

#[cfg(target_arch = "aarch64")]
fn msg_send_super_fn<R>() -> unsafe extern fn(*const Super, Sel, ...) -> R {
    unsafe { mem::transmute(runtime::objc_msgSendSuper) }
}

/// Types that may be used as the arguments of an Objective-C message.
pub trait MessageArguments {
    /// Sends a message to the given obj with the given selector and self as
    /// the arguments.
    ///
    /// The correct version of `objc_msgSend` will be chosen based on the
    /// return type. For more information, see Apple's documenation:
    /// https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ObjCRuntimeRef/index.html#//apple_ref/doc/uid/TP40001418-CH1g-88778
    ///
    /// It is recommended to use the `msg_send!` macro rather than calling this
    /// method directly.
    unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R
            where T: Message, R: Any;

    /// Sends a message to the superclass of an instance of a class with self
    /// as the arguments.
    unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
            where T: Message, R: Any;
}

macro_rules! message_args_impl {
    ($($a:ident : $t:ident),*) => (
        impl<$($t),*> MessageArguments for ($($t,)*) {
            unsafe fn send<T, R>(self, obj: *mut T, sel: Sel) -> R
                    where T: Message, R: Any {
                let msg_send_fn = msg_send_fn::<R>();
                let msg_send_fn: unsafe extern fn(*mut Object, Sel $(, $t)*) -> R =
                    mem::transmute(msg_send_fn);
                let ($($a,)*) = self;
                objc_try!({
                    msg_send_fn(obj as *mut Object, sel $(, $a)*)
                })
            }

            unsafe fn send_super<T, R>(self, obj: *mut T, superclass: &Class, sel: Sel) -> R
                    where T: Message, R: Any {
                let msg_send_fn = msg_send_super_fn::<R>();
                let msg_send_fn: unsafe extern fn(*const Super, Sel $(, $t)*) -> R =
                    mem::transmute(msg_send_fn);
                let sup = Super { receiver: obj as *mut Object, superclass: superclass };
                let ($($a,)*) = self;
                objc_try!({
                    msg_send_fn(&sup, sel $(, $a)*)
                })
            }
        }
    );
}

message_args_impl!();
message_args_impl!(a: A);
message_args_impl!(a: A, b: B);
message_args_impl!(a: A, b: B, c: C);
message_args_impl!(a: A, b: B, c: C, d: D);
message_args_impl!(a: A, b: B, c: C, d: D, e: E);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
message_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

#[cfg(test)]
mod tests {
    use runtime::Object;
    use test_utils;

    #[test]
    fn test_send_message() {
        let obj = test_utils::sample_object();
        let result: *const Object = unsafe {
            msg_send![obj, self]
        };
        assert!(result == &*obj);
    }

    #[test]
    fn test_send_message_stret() {
        let obj = test_utils::custom_object();
        let result: test_utils::CustomStruct = unsafe {
            msg_send![obj, customStruct]
        };
        let expected = test_utils::CustomStruct { a: 1, b:2, c: 3, d: 4 };
        assert!(result == expected);
    }

    #[test]
    fn test_send_message_nil() {
        let nil: *mut Object = ::std::ptr::null_mut();
        let result: usize = unsafe {
            msg_send![nil, hash]
        };
        assert!(result == 0);

        let result: *mut Object = unsafe {
            msg_send![nil, description]
        };
        assert!(result.is_null());

        let result: f64 = unsafe {
            msg_send![nil, doubleValue]
        };
        assert!(result == 0.0);
    }

    #[test]
    fn test_send_message_super() {
        let obj = test_utils::custom_subclass_object();
        let superclass = test_utils::custom_class();
        unsafe {
            let _: () = msg_send![obj, setFoo:4u32];
            let foo: u32 = msg_send![super(obj, superclass), foo];
            assert!(foo == 4);

            // The subclass is overriden to return foo + 2
            let foo: u32 = msg_send![obj, foo];
            assert!(foo == 6);
        }
    }
}
<|endoftext|>