<commit_before>use clap::App;

fn main() {
    // You can use some convenience methods provided by clap to get typed values, so long as the
    // type you're converting into implements std::str::FromStr
    //
    // This works for both single, and multiple values (multiple values returns a Vec<T>)
    //
    // There are also two ways in which to get types, those where failures cause the program to exit
    // with an error and usage string, and those which return a Result<T,String> or Result<Vec<T>,String>
    // respectively. Both methods support single and multiple values.
    //
    // The method which returns a Result allows you decide what to do upon a failure, exit, provide a
    // default value, etc. You have control. But it also means you have to write the code or boiler plate
    // to handle those instances.
    //
    // That is why the second method exists, so you can simply get a T or Vec<T> back, or be sure the
    // program will exit gracefully. The catch is, the second method should *only* be used on required
    // arguments, because if the argument isn't found, it exits. Just FYI ;)
    //
    // The following example shows both methods.
    let matches = App::new("myapp")
        // Create two arguments, a required positional which accepts multiple values
        // and an optional '-l value'
        .arg("<seq>... 'A sequence of whole positive numbers, i.e. 20 25 30'")
        .arg("-l [len] 'A length to use, defaults to 10 when omitted'")
        .get_matches();

    // Here we get a value of type u32 from our optional -l argument.
    // If the value provided to len fails to parse or not present, we default to 10
    //
    // Using other methods such as unwrap_or_else(|e| println!("{}",e))
    // are possible too.
    let len: u32 = matches.value_of_t("l").unwrap_or(10);

    println!("len ({}) + 2 = {}", len, len + 2);

    // This code loops through all the values provided to "seq" and adds 2
    // If seq fails to parse, the program exits, you don't have an option
    for v in matches
        .values_of_t::<u32>("seq")
        .unwrap_or_else(|e| e.exit())
    {
        println!("Sequence part {} + 2: {}", v, v + 2);
    }
}
<commit_msg>docs(examples): Be consistent in define and read order<commit_after>use clap::App;

fn main() {
    // You can use some convenience methods provided by clap to get typed values, so long as the
    // type you're converting into implements std::str::FromStr
    //
    // This works for both single, and multiple values (multiple values returns a Vec<T>)
    //
    // There are also two ways in which to get types, those where failures cause the program to exit
    // with an error and usage string, and those which return a Result<T,String> or Result<Vec<T>,String>
    // respectively. Both methods support single and multiple values.
    //
    // The method which returns a Result allows you decide what to do upon a failure, exit, provide a
    // default value, etc. You have control. But it also means you have to write the code or boiler plate
    // to handle those instances.
    //
    // That is why the second method exists, so you can simply get a T or Vec<T> back, or be sure the
    // program will exit gracefully. The catch is, the second method should *only* be used on required
    // arguments, because if the argument isn't found, it exits. Just FYI ;)
    //
    // The following example shows both methods.
    let matches = App::new("myapp")
        // Create two arguments, a required positional which accepts multiple values
        // and an optional '-l value'
        .arg("<seq>... 'A sequence of whole positive numbers, i.e. 20 25 30'")
        .arg("-l [len] 'A length to use, defaults to 10 when omitted'")
        .get_matches();

    // This code loops through all the values provided to "seq" and adds 2
    // If seq fails to parse, the program exits, you don't have an option
    for v in matches
        .values_of_t::<u32>("seq")
        .unwrap_or_else(|e| e.exit())
    {
        println!("Sequence part {} + 2: {}", v, v + 2);
    }

    // Here we get a value of type u32 from our optional -l argument.
    // If the value provided to len fails to parse or not present, we default to 10
    //
    // Using other methods such as unwrap_or_else(|e| println!("{}",e))
    // are possible too.
    let len: u32 = matches.value_of_t("l").unwrap_or(10);

    println!("len ({}) + 2 = {}", len, len + 2);
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "macos", target_os = "freebsd")))]
    fn posix_spawn(&mut self, _stdio: &ChildPipes, _envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    // Only support platforms for which posix_spawn() can return ENOENT
    // directly.
    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(*sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::from_raw_os_error(ret))
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<commit_msg>Use _<commit_after>// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use io::{self, Error, ErrorKind};
use libc::{self, c_int, gid_t, pid_t, uid_t};
use ptr;

use sys::cvt;
use sys::process::process_common::*;

////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////

impl Command {
    pub fn spawn(&mut self, default: Stdio, needs_stdin: bool)
                 -> io::Result<(Process, StdioPipes)> {
        use sys;

        const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX";

        let envp = self.capture_env();

        if self.saw_nul() {
            return Err(io::Error::new(ErrorKind::InvalidInput,
                                      "nul byte found in provided data"));
        }

        let (ours, theirs) = self.setup_io(default, needs_stdin)?;

        if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
            return Ok((ret, ours))
        }

        let (input, output) = sys::pipe::anon_pipe()?;

        let pid = unsafe {
            match cvt(libc::fork())? {
                0 => {
                    drop(input);
                    let err = self.do_exec(theirs, envp.as_ref());
                    let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
                    let bytes = [
                        (errno >> 24) as u8,
                        (errno >> 16) as u8,
                        (errno >>  8) as u8,
                        (errno >>  0) as u8,
                        CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1],
                        CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3]
                    ];
                    // pipe I/O up to PIPE_BUF bytes should be atomic, and then
                    // we want to be sure we *don't* run at_exit destructors as
                    // we're being torn down regardless
                    assert!(output.write(&bytes).is_ok());
                    libc::_exit(1)
                }
                n => n,
            }
        };

        let mut p = Process { pid: pid, status: None };
        drop(output);
        let mut bytes = [0; 8];

        // loop to handle EINTR
        loop {
            match input.read(&mut bytes) {
                Ok(0) => return Ok((p, ours)),
                Ok(8) => {
                    assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]),
                            "Validation on the CLOEXEC pipe failed: {:?}", bytes);
                    let errno = combine(&bytes[0.. 4]);
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    return Err(Error::from_raw_os_error(errno))
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => {
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("the CLOEXEC pipe failed: {:?}", e)
                },
                Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic
                    assert!(p.wait().is_ok(),
                            "wait() should either return Ok or panic");
                    panic!("short read on the CLOEXEC pipe")
                }
            }
        }

        fn combine(arr: &[u8]) -> i32 {
            let a = arr[0] as u32;
            let b = arr[1] as u32;
            let c = arr[2] as u32;
            let d = arr[3] as u32;

            ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
        }
    }

    pub fn exec(&mut self, default: Stdio) -> io::Error {
        let envp = self.capture_env();

        if self.saw_nul() {
            return io::Error::new(ErrorKind::InvalidInput,
                                  "nul byte found in provided data")
        }

        match self.setup_io(default, true) {
            Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) },
            Err(e) => e,
        }
    }

    // And at this point we've reached a special time in the life of the
    // child. The child must now be considered hamstrung and unable to
    // do anything other than syscalls really. Consider the following
    // scenario:
    //
    //      1. Thread A of process 1 grabs the malloc() mutex
    //      2. Thread B of process 1 forks(), creating thread C
    //      3. Thread C of process 2 then attempts to malloc()
    //      4. The memory of process 2 is the same as the memory of
    //         process 1, so the mutex is locked.
    //
    // This situation looks a lot like deadlock, right? It turns out
    // that this is what pthread_atfork() takes care of, which is
    // presumably implemented across platforms. The first thing that
    // threads to *before* forking is to do things like grab the malloc
    // mutex, and then after the fork they unlock it.
    //
    // Despite this information, libnative's spawn has been witnessed to
    // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
    // all collected backtraces point at malloc/free traffic in the
    // child spawned process.
    //
    // For this reason, the block of code below should contain 0
    // invocations of either malloc of free (or their related friends).
    //
    // As an example of not having malloc/free traffic, we don't close
    // this file descriptor by dropping the FileDesc (which contains an
    // allocation). Instead we just close it manually. This will never
    // have the drop glue anyway because this code never returns (the
    // child will either exec() or invoke libc::exit)
    unsafe fn do_exec(
        &mut self,
        stdio: ChildPipes,
        maybe_envp: Option<&CStringArray>
    ) -> io::Error {
        use sys::{self, cvt_r};

        macro_rules! t {
            ($e:expr) => (match $e {
                Ok(e) => e,
                Err(e) => return e,
            })
        }

        if let Some(fd) = stdio.stdin.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO)));
        }
        if let Some(fd) = stdio.stdout.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO)));
        }
        if let Some(fd) = stdio.stderr.fd() {
            t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO)));
        }

        if cfg!(not(any(target_os = "l4re"))) {
            if let Some(u) = self.get_gid() {
                t!(cvt(libc::setgid(u as gid_t)));
            }
            if let Some(u) = self.get_uid() {
                // When dropping privileges from root, the `setgroups` call
                // will remove any extraneous groups. If we don't call this,
                // then even though our uid has dropped, we may still have
                // groups that enable us to do super-user things. This will
                // fail if we aren't root, so don't bother checking the
                // return value, this is just done as an optimistic
                // privilege dropping function.
                let _ = libc::setgroups(0, ptr::null());

                t!(cvt(libc::setuid(u as uid_t)));
            }
        }
        if let Some(ref cwd) = *self.get_cwd() {
            t!(cvt(libc::chdir(cwd.as_ptr())));
        }
        if let Some(envp) = maybe_envp {
            *sys::os::environ() = envp.as_ptr();
        }

        // emscripten has no signal support.
        #[cfg(not(any(target_os = "emscripten")))]
        {
            use mem;
            // Reset signal handling so the child process starts in a
            // standardized state. libstd ignores SIGPIPE, and signal-handling
            // libraries often set a mask. Child processes inherit ignored
            // signals and the signal mask from their parent, but most
            // UNIX programs do not reset these things on their own, so we
            // need to clean things up now to avoid confusing the program
            // we're about to run.
            let mut set: libc::sigset_t = mem::uninitialized();
            if cfg!(target_os = "android") {
                // Implementing sigemptyset allow us to support older Android
                // versions. See the comment about Android and sig* functions in
                // process_common.rs
                libc::memset(&mut set as *mut _ as *mut _,
                             0,
                             mem::size_of::<libc::sigset_t>());
            } else {
                t!(cvt(libc::sigemptyset(&mut set)));
            }
            t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set,
                                         ptr::null_mut())));
            let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
            if ret == libc::SIG_ERR {
                return io::Error::last_os_error()
            }
        }

        for callback in self.get_closures().iter_mut() {
            t!(callback());
        }

        libc::execvp(self.get_argv()[0], self.get_argv().as_ptr());
        io::Error::last_os_error()
    }

    #[cfg(not(any(target_os = "macos", target_os = "freebsd")))]
    fn posix_spawn(&mut self, _: &ChildPipes, _: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        Ok(None)
    }

    // Only support platforms for which posix_spawn() can return ENOENT
    // directly.
    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
    fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>)
        -> io::Result<Option<Process>>
    {
        use mem;
        use sys;

        if self.get_cwd().is_some() ||
            self.get_gid().is_some() ||
            self.get_uid().is_some() ||
            self.get_closures().len() != 0 {
            return Ok(None)
        }

        let mut p = Process { pid: 0, status: None };

        struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t);

        impl Drop for PosixSpawnFileActions {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawn_file_actions_destroy(&mut self.0);
                }
            }
        }

        struct PosixSpawnattr(libc::posix_spawnattr_t);

        impl Drop for PosixSpawnattr {
            fn drop(&mut self) {
                unsafe {
                    libc::posix_spawnattr_destroy(&mut self.0);
                }
            }
        }

        unsafe {
            let mut file_actions = PosixSpawnFileActions(mem::uninitialized());
            let mut attrs = PosixSpawnattr(mem::uninitialized());

            libc::posix_spawnattr_init(&mut attrs.0);
            libc::posix_spawn_file_actions_init(&mut file_actions.0);

            if let Some(fd) = stdio.stdin.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDIN_FILENO))?;
            }
            if let Some(fd) = stdio.stdout.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDOUT_FILENO))?;
            }
            if let Some(fd) = stdio.stderr.fd() {
                cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0,
                                                           fd,
                                                           libc::STDERR_FILENO))?;
            }

            let mut set: libc::sigset_t = mem::uninitialized();
            cvt(libc::sigemptyset(&mut set))?;
            cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0,
                                                 &set))?;
            cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?;
            cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0,
                                                    &set))?;

            let flags = libc::POSIX_SPAWN_SETSIGDEF |
                libc::POSIX_SPAWN_SETSIGMASK;
            cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?;

            let envp = envp.map(|c| c.as_ptr())
                .unwrap_or(*sys::os::environ() as *const _);
            let ret = libc::posix_spawnp(
                &mut p.pid,
                self.get_argv()[0],
                &file_actions.0,
                &attrs.0,
                self.get_argv().as_ptr() as *const _,
                envp as *const _,
            );
            if ret == 0 {
                Ok(Some(p))
            } else {
                Err(io::Error::from_raw_os_error(ret))
            }
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Processes
////////////////////////////////////////////////////////////////////////////////

/// The unique id of the process (this should never be negative).
pub struct Process {
    pid: pid_t,
    status: Option<ExitStatus>,
}

impl Process {
    pub fn id(&self) -> u32 {
        self.pid as u32
    }

    pub fn kill(&mut self) -> io::Result<()> {
        // If we've already waited on this process then the pid can be recycled
        // and used for another process, and we probably shouldn't be killing
        // random processes, so just return an error.
        if self.status.is_some() {
            Err(Error::new(ErrorKind::InvalidInput,
                           "invalid argument: can't kill an exited process"))
        } else {
            cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ())
        }
    }

    pub fn wait(&mut self) -> io::Result<ExitStatus> {
        use sys::cvt_r;
        if let Some(status) = self.status {
            return Ok(status)
        }
        let mut status = 0 as c_int;
        cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
        self.status = Some(ExitStatus::new(status));
        Ok(ExitStatus::new(status))
    }

    pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
        if let Some(status) = self.status {
            return Ok(Some(status))
        }
        let mut status = 0 as c_int;
        let pid = cvt(unsafe {
            libc::waitpid(self.pid, &mut status, libc::WNOHANG)
        })?;
        if pid == 0 {
            Ok(None)
        } else {
            self.status = Some(ExitStatus::new(status));
            Ok(Some(ExitStatus::new(status)))
        }
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(docopt_macros)]

extern crate difference;
extern crate docopt;
extern crate rust_rosetta;
extern crate rustc_serialize;
extern crate term;

extern crate meta;

use std::io;

use difference::Difference;
use term::Terminal;

use meta::Task;

docopt!(Args derive Debug, "
Detect unimplemented tasks.

This script prints out the name of each task, followed by whether it is implemented online,
locally, or both.

Tasks must be specified using normalized names, e.g. \"K-d tree\". If no tasks are specified,
determines the status for all tasks.

Optionally prints out a diff as well.

Usage:
    coverage [options] [<tasks>...]

Options:
    -h --help           Show this screen.

    --diff              Print diffs.

    --filter=<type>     Filter tasks printed by the program. Accepted values:

                            all                 Print all tasks (default).

                            localonly           Only print tasks that are implemented locally, but
                                                not on the wiki.

                            remoteonly          Only print tasks that are implemented on the wiki,
                                                but not locally.

                            unimplemented       Only print tasks that neither implemented locally
                                                nor remotely.
", flag_filter: Option<TaskFilter>);

#[derive(Debug, Clone, RustcDecodable)]
enum TaskFilter {
    All,
    LocalOnly,
    RemoteOnly,
    Unimplemented,
}

impl Default for TaskFilter {
    fn default() -> Self {
        TaskFilter::All
    }
}

/// Prints a colored diff of two strings to the terminal.
fn print_diff<T: ?Sized>(t: &mut T, s1: &str, s2: &str) -> io::Result<()>
    where T: Terminal
{
    let (_dist, changeset) = difference::diff(&s1, &s2, "\n");

    for i in 0..changeset.len() {
        match changeset[i] {
            Difference::Same(ref x) => {
                try!(t.reset());
                try!(writeln!(t, " {}", x));
            }
            Difference::Add(ref x) => {
                try!(t.fg(term::color::GREEN));
                for line in x.split("\n") {
                    try!(writeln!(t, "+{}", line));
                }
            }
            Difference::Rem(ref x) => {
                try!(t.fg(term::color::RED));
                for line in x.split("\n") {
                    try!(writeln!(t, "-{}", line));
                }
            }
        }
    }
    try!(t.reset());
    try!(t.flush());
    Ok(())
}

/// Prints a task in a human-readable format.
fn print_task<T: ?Sized>(t: &mut T, task: &Task, diff: bool) -> io::Result<()>
    where T: Terminal
{
    try!(t.attr(term::Attr::Bold));
    try!(writeln!(t, "{}", task.title()));
    try!(t.reset());

    try!(write!(t, "Local:"));
    try!(write_status(t, task.local_code().is_some()));

    try!(write!(t, "Remote:"));
    try!(write_status(t, task.remote_code().is_some()));
    try!(writeln!(t, ""));

    if let (Some(ref local_code), Some(ref remote_code)) = (task.local_code(), task.remote_code()) {
        if diff {
            try!(print_diff(t, remote_code, local_code));
        }
    }

    Ok(())
}

/// Writes a boolean as a pretty, human-readable string.
fn write_status<T: ?Sized>(t: &mut T, boolean: bool) -> io::Result<()>
    where T: Terminal
{
    try!(t.attr(term::Attr::Bold));

    if boolean {
        try!(t.fg(term::color::GREEN));
        try!(write!(t, " ✔ "))
    } else {
        try!(t.fg(term::color::RED));
        try!(write!(t, " ✘ "));
    }

    try!(t.reset());
    Ok(())
}

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

    let mut t = term::stdout().unwrap();

    let tasks = if args.arg_tasks.len() > 0 {
        meta::fetch_tasks(&args.arg_tasks.as_slice())
    } else {
        meta::fetch_all_tasks()
    };

    let task_filter = args.flag_filter.unwrap_or_default().to_owned();

    for task in tasks {
        match task_filter {
            TaskFilter::LocalOnly if !task.is_local_only() => continue,
            TaskFilter::RemoteOnly if !task.is_remote_only() => continue,
            TaskFilter::Unimplemented if !task.is_unimplemented() => continue,
            TaskFilter::All | _ => print_task(&mut *t, &task, args.flag_diff).unwrap(),
        }
    }
}
<commit_msg>remove `coverage`'s dependency on rust-rosetta'<commit_after>#![feature(plugin)]
#![plugin(docopt_macros)]

extern crate difference;
extern crate docopt;
extern crate rustc_serialize;
extern crate term;

extern crate meta;

use std::io;

use difference::Difference;
use term::Terminal;

use meta::Task;

docopt!(Args derive Debug, "
Detect unimplemented tasks.

This script prints out the name of each task, followed by whether it is implemented online,
locally, or both.

Tasks must be specified using normalized names, e.g. \"K-d tree\". If no tasks are specified,
determines the status for all tasks.

Optionally prints out a diff as well.

Usage:
    coverage [options] [<tasks>...]

Options:
    -h --help           Show this screen.

    --diff              Print diffs.

    --filter=<type>     Filter tasks printed by the program. Accepted values:

                            all                 Print all tasks (default).

                            localonly           Only print tasks that are implemented locally, but
                                                not on the wiki.

                            remoteonly          Only print tasks that are implemented on the wiki,
                                                but not locally.

                            unimplemented       Only print tasks that neither implemented locally
                                                nor remotely.
", flag_filter: Option<TaskFilter>);

#[derive(Debug, Clone, RustcDecodable)]
enum TaskFilter {
    All,
    LocalOnly,
    RemoteOnly,
    Unimplemented,
}

impl Default for TaskFilter {
    fn default() -> Self {
        TaskFilter::All
    }
}

/// Prints a colored diff of two strings to the terminal.
fn print_diff<T: ?Sized>(t: &mut T, s1: &str, s2: &str) -> io::Result<()>
    where T: Terminal
{
    let (_dist, changeset) = difference::diff(&s1, &s2, "\n");

    for i in 0..changeset.len() {
        match changeset[i] {
            Difference::Same(ref x) => {
                try!(t.reset());
                try!(writeln!(t, " {}", x));
            }
            Difference::Add(ref x) => {
                try!(t.fg(term::color::GREEN));
                for line in x.split("\n") {
                    try!(writeln!(t, "+{}", line));
                }
            }
            Difference::Rem(ref x) => {
                try!(t.fg(term::color::RED));
                for line in x.split("\n") {
                    try!(writeln!(t, "-{}", line));
                }
            }
        }
    }
    try!(t.reset());
    try!(t.flush());
    Ok(())
}

/// Prints a task in a human-readable format.
fn print_task<T: ?Sized>(t: &mut T, task: &Task, diff: bool) -> io::Result<()>
    where T: Terminal
{
    try!(t.attr(term::Attr::Bold));
    try!(writeln!(t, "{}", task.title()));
    try!(t.reset());

    try!(write!(t, "Local:"));
    try!(write_status(t, task.local_code().is_some()));

    try!(write!(t, "Remote:"));
    try!(write_status(t, task.remote_code().is_some()));
    try!(writeln!(t, ""));

    if let (Some(ref local_code), Some(ref remote_code)) = (task.local_code(), task.remote_code()) {
        if diff {
            try!(print_diff(t, remote_code, local_code));
        }
    }

    Ok(())
}

/// Writes a boolean as a pretty, human-readable string.
fn write_status<T: ?Sized>(t: &mut T, boolean: bool) -> io::Result<()>
    where T: Terminal
{
    try!(t.attr(term::Attr::Bold));

    if boolean {
        try!(t.fg(term::color::GREEN));
        try!(write!(t, " ✔ "))
    } else {
        try!(t.fg(term::color::RED));
        try!(write!(t, " ✘ "));
    }

    try!(t.reset());
    Ok(())
}

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

    let mut t = term::stdout().unwrap();

    let tasks = if args.arg_tasks.len() > 0 {
        meta::fetch_tasks(&args.arg_tasks.as_slice())
    } else {
        meta::fetch_all_tasks()
    };

    let task_filter = args.flag_filter.unwrap_or_default().to_owned();

    for task in tasks {
        match task_filter {
            TaskFilter::LocalOnly if !task.is_local_only() => continue,
            TaskFilter::RemoteOnly if !task.is_remote_only() => continue,
            TaskFilter::Unimplemented if !task.is_unimplemented() => continue,
            TaskFilter::All | _ => print_task(&mut *t, &task, args.flag_diff).unwrap(),
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::collections::VecDeque;
use secp256k1::key::PublicKey;
use vec_map::VecMap;
use bit_set::BitSet;

use messages::*;
use super::*;
use self::history::*;

mod history;
mod peer;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum DcPhase {
    Exponential,
    Main,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum RunState {
    DcProcess(DcPhase),
    DcReveal(DcPhase),
    Blame,
    Confirm,
}

impl PartialOrd for RunState {
    fn partial_cmp(&self, other: &RunState) -> Option<Ordering> {
        // This is ugly but not uglier than using std::intrinsics::discriminant_value,
        // which does guarantee a proper ordering and would force us to use debug assertions
        // anyway. Note that std::mem::Discriminant<T> does not implement PartialOrd either,
        // because it relies on std::intrinsics::discriminant_value.
        // If this changes in the future, we can replace this function.
        #[inline]
        fn discriminant(x: &RunState) -> u32 {
            match *x {
                RunState::DcProcess(DcPhase::Exponential) => 0,
                RunState::DcReveal(DcPhase::Exponential) => 1,
                RunState::DcProcess(DcPhase::Main) => 2,
                RunState::DcReveal(DcPhase::Main) => 3,
                RunState::Blame => 4,
                RunState::Confirm => 5,
            }
        }

        match (*self, *other) {
            (RunState::Blame, RunState::Confirm) => None,
            (RunState::Confirm, RunState::Blame) => None,
            _ => discriminant(self).partial_cmp(&discriminant(other)),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct RunStateMachine {
    run: RunCounter,
    state: RunState,
    kepks: VecMap<PublicKey>,
    received: BitSet,
    otvk_hashes: Option<Vec<[u8; 32]>>,
    peers_before_dc_exponential: Option<BitSet>,
    peers_before_dc_main: Option<BitSet>,
}

impl RunStateMachine {
    fn new(&mut self, run: RunCounter, kepks: VecMap<PublicKey>) -> Self {
        let num_peers = kepks.len();
        let new = Self {
            run: 0,
            state: RunState::DcProcess(DcPhase::Exponential),
            kepks: kepks,
            received: BitSet::with_capacity(num_peers),
            otvk_hashes: None,
            peers_before_dc_exponential: None,
            peers_before_dc_main: None,
        };
        debug_assert!(new.consistent());
        new
    }

    #[inline]
    fn num_peers(&self) -> usize {
        self.kepks.len()
    }

    #[inline]
    fn set_state(&mut self, state: RunState) {
        assert!(self.state < state);
        self.state = state;
    }

    #[inline]
    fn consistent(&self) -> bool {
        // TODO check also the state ...
        match (self.peers_before_dc_exponential.is_some(),
               self.otvk_hashes.is_some(),
               self.peers_before_dc_main.is_some()) {
            (false, false, false) => true,
            (false, _, _) => false,
            (true, x, y) => x == y,
        }
    }

    fn process_message(&mut self, msg: Message) -> Option<Payload> {
        // The message has a correct signature and is intended for this state of this session.
        // So we can record it.
        let first_from_peer = self.received.insert(msg.header.peer_index as usize);
        // The stream should never send us two messages from the same peer in the same round.
        debug_assert!(first_from_peer);

        self.process_payload(msg.payload)
    }

    fn process_payload(&self, payload: Payload) -> Option<Payload> {
        match(self.state, payload) {
            (RunState::DcProcess(DcPhase::Exponential), Payload::DcExponential(msg)) => {
                unimplemented!()
            },
            (RunState::DcProcess(DcPhase::Main), Payload::DcMain(msg)) => {
                unimplemented!()
            },
            (RunState::DcReveal(phase), Payload::Reveal(msg)) => {
                unimplemented!()
            },
            (RunState::Blame, Payload::Blame(msg)) => {
                unimplemented!()
            },
            (RunState::Confirm, Payload::Confirm(msg)) => {
                unimplemented!()
            },
            _ => {
                // TODO Kick the peer out
                unimplemented!()
            }
        }
        assert!(self.consistent());
    }
}

<commit_msg>Correct wrong comment<commit_after>use std::cmp::Ordering;
use std::collections::VecDeque;
use secp256k1::key::PublicKey;
use vec_map::VecMap;
use bit_set::BitSet;

use messages::*;
use super::*;
use self::history::*;

mod history;
mod peer;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum DcPhase {
    Exponential,
    Main,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum RunState {
    DcProcess(DcPhase),
    DcReveal(DcPhase),
    Blame,
    Confirm,
}

impl PartialOrd for RunState {
    fn partial_cmp(&self, other: &RunState) -> Option<Ordering> {
        // This is ugly but not uglier than using std::intrinsics::discriminant_value,
        // which does not guarantee a proper ordering and consequently would force us to use
        // debug assertions to make sure that the compiler actually uses proper ordering
        // internally. Note that std::mem::Discriminant<T> does not implement PartialOrd either,
        // because it relies on std::intrinsics::discriminant_value.
        // If this changes in the future, we can replace this function.
        #[inline]
        fn discriminant(x: &RunState) -> u32 {
            match *x {
                RunState::DcProcess(DcPhase::Exponential) => 0,
                RunState::DcReveal(DcPhase::Exponential) => 1,
                RunState::DcProcess(DcPhase::Main) => 2,
                RunState::DcReveal(DcPhase::Main) => 3,
                RunState::Blame => 4,
                RunState::Confirm => 5,
            }
        }

        match (*self, *other) {
            (RunState::Blame, RunState::Confirm) => None,
            (RunState::Confirm, RunState::Blame) => None,
            _ => discriminant(self).partial_cmp(&discriminant(other)),
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
struct RunStateMachine {
    run: RunCounter,
    state: RunState,
    kepks: VecMap<PublicKey>,
    received: BitSet,
    otvk_hashes: Option<Vec<[u8; 32]>>,
    peers_before_dc_exponential: Option<BitSet>,
    peers_before_dc_main: Option<BitSet>,
}

impl RunStateMachine {
    fn new(&mut self, run: RunCounter, kepks: VecMap<PublicKey>) -> Self {
        let num_peers = kepks.len();
        let new = Self {
            run: 0,
            state: RunState::DcProcess(DcPhase::Exponential),
            kepks: kepks,
            received: BitSet::with_capacity(num_peers),
            otvk_hashes: None,
            peers_before_dc_exponential: None,
            peers_before_dc_main: None,
        };
        debug_assert!(new.consistent());
        new
    }

    #[inline]
    fn num_peers(&self) -> usize {
        self.kepks.len()
    }

    #[inline]
    fn set_state(&mut self, state: RunState) {
        assert!(self.state < state);
        self.state = state;
    }

    #[inline]
    fn consistent(&self) -> bool {
        // TODO check also the state ...
        match (self.peers_before_dc_exponential.is_some(),
               self.otvk_hashes.is_some(),
               self.peers_before_dc_main.is_some()) {
            (false, false, false) => true,
            (false, _, _) => false,
            (true, x, y) => x == y,
        }
    }

    fn process_message(&mut self, msg: Message) -> Option<Payload> {
        // The message has a correct signature and is intended for this state of this session.
        // So we can record it.
        let first_from_peer = self.received.insert(msg.header.peer_index as usize);
        // The stream should never send us two messages from the same peer in the same round.
        debug_assert!(first_from_peer);

        self.process_payload(msg.payload)
    }

    fn process_payload(&self, payload: Payload) -> Option<Payload> {
        match(self.state, payload) {
            (RunState::DcProcess(DcPhase::Exponential), Payload::DcExponential(msg)) => {
                unimplemented!()
            },
            (RunState::DcProcess(DcPhase::Main), Payload::DcMain(msg)) => {
                unimplemented!()
            },
            (RunState::DcReveal(phase), Payload::Reveal(msg)) => {
                unimplemented!()
            },
            (RunState::Blame, Payload::Blame(msg)) => {
                unimplemented!()
            },
            (RunState::Confirm, Payload::Confirm(msg)) => {
                unimplemented!()
            },
            _ => {
                // TODO Kick the peer out
                unimplemented!()
            }
        }
        assert!(self.consistent());
    }
}

<|endoftext|>


<commit_before>extern crate pkg_config;
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{PathBuf, Path};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let src = env::current_dir().unwrap();
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let windows = target.contains("windows");

    // OSX ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if !target.contains("windows") {
        match pkg_config::find_library("libcurl") {
            Ok(lib) => {
                for path in lib.include_paths.iter() {
                    println!("cargo:include={}", path.display());
                }
                return
            }
            Err(e) => println!("Couldn't find libcurl from \
                               pkgconfig ({:?}), compiling it from source...", e),
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    println!("cargo:rustc-link-search={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=curl");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}/include", dst.display());
    if windows {
        println!("cargo:rustc-flags=-l ws2_32");
    }

    // MSVC builds are just totally different
    if target.contains("msvc") {
        return build_msvc(&target);
    }

    let cfg = gcc::Config::new();
    let compiler = cfg.get_compiler();

    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("sh");
    let mut cflags = OsString::new();
    for arg in compiler.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // Can't run ./configure directly on msys2 b/c we're handing in
    // Windows-style paths (those starting with C:\), but it chokes on those.
    // For that reason we build up a shell script with paths converted to
    // posix versions hopefully...
    //
    // Also apparently the buildbots choke unless we manually set LD, who knows
    // why?!
    cmd.env("CC", compiler.path())
       .env("CFLAGS", cflags)
       .env("LD", &which("ld").unwrap())
       .env("VERBOSE", "1")
       .current_dir(&dst.join("build"))
       .arg(src.join("curl/configure").to_str().unwrap()
               .replace("C:\\", "/c/")
               .replace("\\", "/"));

    if windows {
        cmd.arg("--with-winssl");
    } else {
        cmd.arg("--without-ca-bundle");
        cmd.arg("--without-ca-path");

        // DEP_OPENSSL_ROOT was what cargo used to use via overrides, and
        // OPENSSL_ROOT_DIR is also used by libssh2 which cargo uses and can be
        // set by various build scripts.
        let openssl_root = env::var("DEP_OPENSSL_ROOT").or_else(|_| {
            env::var("OPENSSL_ROOT_DIR")
        });
        if let Ok(s) = openssl_root {
            cmd.arg(format!("--with-ssl={}", s));
        }
    }
    cmd.arg("--enable-static=yes");
    cmd.arg("--enable-shared=no");
    cmd.arg("--enable-optimize");
    cmd.arg(format!("--prefix={}", dst.display()));

    if target != host {
        cmd.arg(format!("--host={}", host));
        cmd.arg(format!("--target={}", target));
    }

    cmd.arg("--without-librtmp");
    cmd.arg("--without-libidn");
    cmd.arg("--without-libssh2");
    cmd.arg("--without-nghttp2");
    cmd.arg("--disable-ldap");
    cmd.arg("--disable-ldaps");
    cmd.arg("--disable-ftp");
    cmd.arg("--disable-rtsp");
    cmd.arg("--disable-dict");
    cmd.arg("--disable-telnet");
    cmd.arg("--disable-tftp");
    cmd.arg("--disable-pop3");
    cmd.arg("--disable-imap");
    cmd.arg("--disable-smtp");
    cmd.arg("--disable-gopher");
    cmd.arg("--disable-manual");
    cmd.arg("--disable-smb");
    cmd.arg("--disable-sspi");

    run(&mut cmd);
    run(Command::new(make())
                .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap()))
                .arg("install")
                .current_dir(&dst.join("build")));
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {"gmake"} else {"make"}
}

fn which(cmd: &str) -> Option<PathBuf> {
    let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX);
    let paths = env::var_os("PATH").unwrap();
    env::split_paths(&paths).map(|p| p.join(&cmd)).find(|p| {
        fs::metadata(p).is_ok()
    })
}

fn build_msvc(target: &str) {
    let cmd = gcc::windows_registry::find(target, "nmake.exe");
    let mut cmd = cmd.unwrap_or(Command::new("nmake.exe"));
    let src = env::current_dir().unwrap();
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let machine = if target.starts_with("x86_64") {
        "x64"
    } else if target.starts_with("i686") {
        "x86"
    } else {
        panic!("unknown msvc target: {}", target);
    };

    t!(fs::create_dir_all(dst.join("include/curl")));
    t!(fs::create_dir_all(dst.join("lib")));

    cmd.current_dir(src.join("curl/winbuild"));
    cmd.arg("/f").arg("Makefile.vc")
       .arg("MODE=static")
       .arg("ENABLE_IDN=yes")
       .arg("DEBUG=no")
       .arg("GEN_PDB=no")
       .arg("ENABLE_WINSSL=yes")
       .arg("ENABLE_SSPI=yes")
       .arg(format!("MACHINE={}", machine));

    if let Some(inc) = env::var_os("DEP_Z_ROOT") {
        let inc = PathBuf::from(inc);
        let mut s = OsString::from("WITH_DEVEL=");
        s.push(&inc);
        cmd.arg("WITH_ZLIB=static").arg(s);

        // the build system for curl expects this library to be called
        // zlib_a.lib, so make sure it's named correctly (where libz-sys just
        // produces zlib.lib)
        let _ = fs::remove_file(&inc.join("lib/zlib_a.lib"));
        t!(fs::hard_link(inc.join("lib/zlib.lib"), inc.join("lib/zlib_a.lib")));
    }
    run(&mut cmd);

    let name = format!("libcurl-vc-{}-release-static-zlib-static-\
                        ipv6-sspi-winssl", machine);
    let libs = src.join("curl/builds").join(name);

    t!(fs::copy(libs.join("lib/libcurl_a.lib"), dst.join("lib/curl.lib")));
    for f in t!(fs::read_dir(libs.join("include/curl"))) {
        let path = t!(f).path();
        let dst = dst.join("include/curl").join(path.file_name().unwrap());
        t!(fs::copy(path, dst));
    }
    t!(fs::remove_dir_all(src.join("curl/builds")));
    println!("cargo:rustc-link-lib=wldap32");
    println!("cargo:rustc-link-lib=advapi32");
    println!("cargo:rustc-link-lib=normaliz");
}
<commit_msg>Try to fix more MinGW weirdness<commit_after>extern crate pkg_config;
extern crate gcc;

use std::env;
use std::ffi::OsString;
use std::fs;
use std::path::{PathBuf, Path};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} return the error {}", stringify!($e), e),
    })
}

fn main() {
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let src = env::current_dir().unwrap();
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let windows = target.contains("windows");

    // OSX ships libcurl by default, so we just use that version
    // unconditionally.
    if target.contains("apple") {
        return println!("cargo:rustc-flags=-l curl");
    }

    // Next, fall back and try to use pkg-config if its available.
    if !target.contains("windows") {
        match pkg_config::find_library("libcurl") {
            Ok(lib) => {
                for path in lib.include_paths.iter() {
                    println!("cargo:include={}", path.display());
                }
                return
            }
            Err(e) => println!("Couldn't find libcurl from \
                               pkgconfig ({:?}), compiling it from source...", e),
        }
    }

    if !Path::new("curl/.git").exists() {
        let _ = Command::new("git").args(&["submodule", "update", "--init"])
                                   .status();
    }

    println!("cargo:rustc-link-search={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=curl");
    println!("cargo:root={}", dst.display());
    println!("cargo:include={}/include", dst.display());
    if windows {
        println!("cargo:rustc-flags=-l ws2_32");
    }

    // MSVC builds are just totally different
    if target.contains("msvc") {
        return build_msvc(&target);
    }

    let cfg = gcc::Config::new();
    let compiler = cfg.get_compiler();

    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("sh");
    let mut cflags = OsString::new();
    for arg in compiler.args() {
        cflags.push(arg);
        cflags.push(" ");
    }

    // Can't run ./configure directly on msys2 b/c we're handing in
    // Windows-style paths (those starting with C:\), but it chokes on those.
    // For that reason we build up a shell script with paths converted to
    // posix versions hopefully...
    //
    // Also apparently the buildbots choke unless we manually set LD, who knows
    // why?!
    cmd.env("CC", compiler.path())
       .env("CFLAGS", cflags)
       .env("LD", &which("ld").unwrap())
       .env("VERBOSE", "1")
       .current_dir(&dst.join("build"))
       .arg(msys_compatible(&src.join("curl/configure")));

    if windows {
        cmd.arg("--with-winssl");
    } else {
        cmd.arg("--without-ca-bundle");
        cmd.arg("--without-ca-path");

        // DEP_OPENSSL_ROOT was what cargo used to use via overrides, and
        // OPENSSL_ROOT_DIR is also used by libssh2 which cargo uses and can be
        // set by various build scripts.
        let openssl_root = env::var("DEP_OPENSSL_ROOT").or_else(|_| {
            env::var("OPENSSL_ROOT_DIR")
        });
        if let Ok(s) = openssl_root {
            cmd.arg(format!("--with-ssl={}", s));
        }
    }
    cmd.arg("--enable-static=yes");
    cmd.arg("--enable-shared=no");
    cmd.arg("--enable-optimize");
    cmd.arg(format!("--prefix={}", msys_compatible(&dst)));

    if target != host {
        cmd.arg(format!("--host={}", host));
        cmd.arg(format!("--target={}", target));
    }

    cmd.arg("--without-librtmp");
    cmd.arg("--without-libidn");
    cmd.arg("--without-libssh2");
    cmd.arg("--without-nghttp2");
    cmd.arg("--disable-ldap");
    cmd.arg("--disable-ldaps");
    cmd.arg("--disable-ftp");
    cmd.arg("--disable-rtsp");
    cmd.arg("--disable-dict");
    cmd.arg("--disable-telnet");
    cmd.arg("--disable-tftp");
    cmd.arg("--disable-pop3");
    cmd.arg("--disable-imap");
    cmd.arg("--disable-smtp");
    cmd.arg("--disable-gopher");
    cmd.arg("--disable-manual");
    cmd.arg("--disable-smb");
    cmd.arg("--disable-sspi");

    run(&mut cmd);
    run(Command::new(make())
                .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap()))
                .arg("install")
                .current_dir(&dst.join("build")));
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(t!(cmd.status()).success());
}

fn make() -> &'static str {
    if cfg!(target_os = "freebsd") {"gmake"} else {"make"}
}

fn which(cmd: &str) -> Option<PathBuf> {
    let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX);
    let paths = env::var_os("PATH").unwrap();
    env::split_paths(&paths).map(|p| p.join(&cmd)).find(|p| {
        fs::metadata(p).is_ok()
    })
}

fn msys_compatible(path: &Path) -> String {
    let path = path.to_str().unwrap();
    if !cfg!(windows) {
        return path.to_string()
    }
    path.replace("C:\\", "/c/")
        .replace("\\", "/")
}

fn build_msvc(target: &str) {
    let cmd = gcc::windows_registry::find(target, "nmake.exe");
    let mut cmd = cmd.unwrap_or(Command::new("nmake.exe"));
    let src = env::current_dir().unwrap();
    let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let machine = if target.starts_with("x86_64") {
        "x64"
    } else if target.starts_with("i686") {
        "x86"
    } else {
        panic!("unknown msvc target: {}", target);
    };

    t!(fs::create_dir_all(dst.join("include/curl")));
    t!(fs::create_dir_all(dst.join("lib")));

    cmd.current_dir(src.join("curl/winbuild"));
    cmd.arg("/f").arg("Makefile.vc")
       .arg("MODE=static")
       .arg("ENABLE_IDN=yes")
       .arg("DEBUG=no")
       .arg("GEN_PDB=no")
       .arg("ENABLE_WINSSL=yes")
       .arg("ENABLE_SSPI=yes")
       .arg(format!("MACHINE={}", machine));

    if let Some(inc) = env::var_os("DEP_Z_ROOT") {
        let inc = PathBuf::from(inc);
        let mut s = OsString::from("WITH_DEVEL=");
        s.push(&inc);
        cmd.arg("WITH_ZLIB=static").arg(s);

        // the build system for curl expects this library to be called
        // zlib_a.lib, so make sure it's named correctly (where libz-sys just
        // produces zlib.lib)
        let _ = fs::remove_file(&inc.join("lib/zlib_a.lib"));
        t!(fs::hard_link(inc.join("lib/zlib.lib"), inc.join("lib/zlib_a.lib")));
    }
    run(&mut cmd);

    let name = format!("libcurl-vc-{}-release-static-zlib-static-\
                        ipv6-sspi-winssl", machine);
    let libs = src.join("curl/builds").join(name);

    t!(fs::copy(libs.join("lib/libcurl_a.lib"), dst.join("lib/curl.lib")));
    for f in t!(fs::read_dir(libs.join("include/curl"))) {
        let path = t!(f).path();
        let dst = dst.join("include/curl").join(path.file_name().unwrap());
        t!(fs::copy(path, dst));
    }
    t!(fs::remove_dir_all(src.join("curl/builds")));
    println!("cargo:rustc-link-lib=wldap32");
    println!("cargo:rustc-link-lib=advapi32");
    println!("cargo:rustc-link-lib=normaliz");
}
<|endoftext|>


<commit_before>//! Pull API handler.
use std::str;
use std::sync::Arc;

use futures::{Finished, finished};
use tokio_service::Service;
use tokio_core::net::TcpStream;
use tk_bufstream::IoBuf;
use minihttp::{Error, Request};
use minihttp::{ResponseFn, Status};
use minihttp::enums::Method;
use rustc_serialize::json::{self, Json};

use {Pickler};
use config::ConfigCell;
use response::DebugInfo;
use default_error_page::write_error_page;
use intern::{Topic, Lattice};

use super::{parse_cid, ProcessorPool};
use super::processor::{Action, Delta};

/// Chat Backend Http handler.
#[derive(Clone)]
pub struct ChatBackend {
    pub config: ConfigCell,
    pub chat_pool: ProcessorPool,
}

impl Service for ChatBackend {
    type Request = Request;
    type Response = ResponseFn<Finished<IoBuf<TcpStream>, Error>, TcpStream>;
    type Error = Error;
    type Future = Finished<Self::Response, Error>;

    fn call(&self, req: Request) -> Self::Future {
        let cfg = self.config.get();
        let status = self.serve(&req);
        finished(ResponseFn::new(move |res| {
            let res = Pickler(res, cfg.clone(), DebugInfo::new(&req));
            write_error_page(status, res).done()
        }))
    }
}

impl ChatBackend {

    fn serve(&self, req: &Request) -> Status {
        use self::ChatRoute::*;

        let route = match match_route(&req.method, req.path.as_str()) {
            Some(r) => r,
            None => return Status::NotFound
        };
        let payload = match req.body {
            Some(ref body) => {
                str::from_utf8(&body.data[..]).ok()
                    .and_then(|s| Json::from_str(s).ok())
            }
            None => None
        };
        if route.expect_data() && payload.is_none() {
            return Status::BadRequest;
        }
        let action = match route {
            TopicSubscribe(client_id, topic) => {
                let cid = parse_cid(client_id);
                Action::Subscribe {
                    conn_id: cid,
                    topic: topic,
                }
            }
            TopicUnsubscribe(client_id, topic) => {
                let cid = parse_cid(client_id);
                Action::Unsubscribe {
                    conn_id: cid,
                    topic: topic,
                }
            }
            LatticeSubscribe(client_id, namespace) => {
                let cid = parse_cid(client_id);
                let delta = match decode_delta(req) {
                    Ok(delta) => delta,
                    Err(err) => {
                        info!("Error {:?}", err);
                        return Status::BadRequest
                    }
                };
                self.chat_pool.send(Action::Lattice {
                    namespace: namespace.clone(),
                    delta: delta,
                });
                Action::Attach {
                    namespace: namespace,
                    conn_id: cid,
                }
            }
            LatticeUnsubscribe(client_id, namespace) => {
                let cid = parse_cid(client_id);
                Action::Detach {
                    namespace: namespace,
                    conn_id: cid,
                }
            }
            TopicPublish(topic) => {
                let data = Arc::new(payload.unwrap());
                Action::Publish {
                    topic: topic,
                    data: data,
                }
            }
            LatticeUpdate(namespace) => {
                let delta = match decode_delta(req) {
                    Ok(delta) => delta,
                    Err(err) => {
                        info!("Error {:?}", err);
                        return Status::BadRequest
                    }
                };
                Action::Lattice {
                    namespace: namespace,
                    delta: delta,
                }
            }
        };
        self.chat_pool.send(action);
        Status::NoContent
    }

}

fn decode_delta(req: &Request) -> Result<Delta, json::DecoderError>
{
    let body = req.body.as_ref().unwrap();
    let body = str::from_utf8(&body.data[..]).unwrap();
    json::decode::<Delta>(body)
}

fn match_route(method: &Method, path: &str) -> Option<ChatRoute> {
    use minihttp::enums::Method::*;
    use self::ChatRoute::*;

    if !path.starts_with("/v1/") {
        return None
    }
    let mut it = path.split("/").skip(2);   // skip '' & 'v1'
    let route = match (method, it.next()) {
        (&Post, Some(kind)) => {
            if !it.next().map(|x| x.len() > 0).unwrap_or(false) {
                return None
            }
            match kind {
                "lattice" => {
                    let (_, namespace) = path.split_at("/v1/lattice/".len());
                    let namespace = namespace.replace("/", ".")
                        .parse().unwrap();
                    ChatRoute::LatticeUpdate(namespace)
                }
                "publish" => {
                    let (_, topic) = path.split_at("/v1/publish/".len());
                    let topic = topic.replace("/", ".")
                        .parse().unwrap();
                    TopicPublish(topic)
                }
                _ => return None
            }
        }
        (&Put, Some("connection")) |
        (&Delete, Some("connection")) => {
            let id = match it.next() {
                Some(id) if id.len() > 0 => id,
                _ => return None,
            };
            let kind = it.next();
            if !it.next().map(|x| x.len() > 0).unwrap_or(false) {
                return None
            }
            match kind {
                Some("subscriptions") => {
                    let (_, topic) = path.split_at(
                        "/v1/connection//subscriptions/".len() + id.len());
                    let topic = topic.replace("/", ".")
                        .parse().unwrap();
                    if method == &Put {
                        TopicSubscribe(id.to_string(), topic)
                    } else {
                        TopicUnsubscribe(id.to_string(), topic)
                    }
                }
                Some("lattices") => {
                    let (_, namespace) = path.split_at(
                        "/v1/connection//lattices/".len() + id.len());
                    let namespace = namespace.replace("/", ".")
                        .parse().unwrap();
                    if method == &Put {
                        LatticeSubscribe(
                            id.to_string(), namespace)
                    } else {
                        LatticeUnsubscribe(
                            id.to_string(), namespace)
                    }
                }
                _ => return None
            }
        }
        _ => return None
    };
    Some(route)
}

#[derive(Debug, PartialEq)]
pub enum ChatRoute {
    TopicSubscribe(String, Topic),
    TopicUnsubscribe(String, Topic),
    LatticeSubscribe(String, Lattice),
    LatticeUnsubscribe(String, Lattice),
    TopicPublish(Topic),
    LatticeUpdate(Lattice),
}

impl ChatRoute {
    fn expect_data(&self) -> bool {
        use self::ChatRoute::*;
        match *self {
            TopicSubscribe(_, _) => false,
            TopicUnsubscribe(_, _) => false,
            LatticeSubscribe(_, _) => true,
            LatticeUnsubscribe(_, _) => false,
            TopicPublish(_) => true,
            LatticeUpdate(_) => true,
        }
    }
}

#[cfg(test)]
mod test {
    use minihttp::enums::Method;
    use string_intern::Symbol;

    use super::match_route;
    use super::ChatRoute::*;

    #[test]
    fn match_topic_publish() {
        let path = "/v1/publish/test-chat/room1";
        let route = match_route(&Method::Post, path).unwrap();
        assert_eq!(route, TopicPublish(Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_update() {
        let path = "/v1/lattice/test-chat/rooms";
        let route = match_route(&Method::Post, path).unwrap();
        assert_eq!(route, LatticeUpdate(Symbol::from("test-chat.rooms")));
    }

    #[test]
    fn match_topic_subscribe() {
        let path = "/v1/connection/abcde/subscriptions/test-chat/room1";
        let route = match_route(&Method::Put, path).unwrap();
        assert_eq!(route, TopicSubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_topic_unsubscribe() {
        let path = "/v1/connection/abcde/subscriptions/test-chat/room1";
        let route = match_route(&Method::Delete, path).unwrap();
        assert_eq!(route, TopicUnsubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_subscribe() {
        let path = "/v1/connection/abcde/lattices/test-chat/room1";
        let route = match_route(&Method::Put, path).unwrap();
        assert_eq!(route, LatticeSubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_unsubscribe() {
        let path = "/v1/connection/abcde/lattices/test-chat/room1";
        let route = match_route(&Method::Delete, path).unwrap();
        assert_eq!(route, LatticeUnsubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn no_matches() {
        let path = "/v/";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/publish";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/publish/";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/publish/test-chat/room1";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/lattice/test-chat/rooms";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/connections//subscriptions/";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());

        let path = "/v1/connections/abc/subscriptions/";
        assert!(match_route(&Method::Get, path).is_none());
        assert!(match_route(&Method::Put, path).is_none());
        assert!(match_route(&Method::Post, path).is_none());
        assert!(match_route(&Method::Patch, path).is_none());
        assert!(match_route(&Method::Delete, path).is_none());
    }
}
<commit_msg>Fix chat backend match_route: do not unwrap<commit_after>//! Pull API handler.
use std::str;
use std::sync::Arc;

use futures::{Finished, finished};
use tokio_service::Service;
use tokio_core::net::TcpStream;
use tk_bufstream::IoBuf;
use minihttp::{Error, Request};
use minihttp::{ResponseFn, Status};
use minihttp::enums::Method;
use rustc_serialize::json::{self, Json};

use {Pickler};
use config::ConfigCell;
use response::DebugInfo;
use default_error_page::write_error_page;
use intern::{Topic, Lattice, BadIdent};

use super::{parse_cid, ProcessorPool};
use super::processor::{Action, Delta};

/// Chat Backend Http handler.
#[derive(Clone)]
pub struct ChatBackend {
    pub config: ConfigCell,
    pub chat_pool: ProcessorPool,
}

impl Service for ChatBackend {
    type Request = Request;
    type Response = ResponseFn<Finished<IoBuf<TcpStream>, Error>, TcpStream>;
    type Error = Error;
    type Future = Finished<Self::Response, Error>;

    fn call(&self, req: Request) -> Self::Future {
        let cfg = self.config.get();
        let status = self.serve(&req);
        finished(ResponseFn::new(move |res| {
            let res = Pickler(res, cfg.clone(), DebugInfo::new(&req));
            write_error_page(status, res).done()
        }))
    }
}

impl ChatBackend {

    fn serve(&self, req: &Request) -> Status {
        use self::ChatRoute::*;

        let route = match match_route(&req.method, req.path.as_str()) {
            Ok(r) => r,
            Err(_) => return Status::NotFound
        };
        let payload = match req.body {
            Some(ref body) => {
                str::from_utf8(&body.data[..]).ok()
                    .and_then(|s| Json::from_str(s).ok())
            }
            None => None
        };
        if route.expect_data() && payload.is_none() {
            return Status::BadRequest;
        }
        let action = match route {
            TopicSubscribe(client_id, topic) => {
                let cid = parse_cid(client_id);
                Action::Subscribe {
                    conn_id: cid,
                    topic: topic,
                }
            }
            TopicUnsubscribe(client_id, topic) => {
                let cid = parse_cid(client_id);
                Action::Unsubscribe {
                    conn_id: cid,
                    topic: topic,
                }
            }
            LatticeSubscribe(client_id, namespace) => {
                let cid = parse_cid(client_id);
                let delta = match decode_delta(req) {
                    Ok(delta) => delta,
                    Err(err) => {
                        info!("Error {:?}", err);
                        return Status::BadRequest
                    }
                };
                self.chat_pool.send(Action::Lattice {
                    namespace: namespace.clone(),
                    delta: delta,
                });
                Action::Attach {
                    namespace: namespace,
                    conn_id: cid,
                }
            }
            LatticeUnsubscribe(client_id, namespace) => {
                let cid = parse_cid(client_id);
                Action::Detach {
                    namespace: namespace,
                    conn_id: cid,
                }
            }
            TopicPublish(topic) => {
                let data = Arc::new(payload.unwrap());
                Action::Publish {
                    topic: topic,
                    data: data,
                }
            }
            LatticeUpdate(namespace) => {
                let delta = match decode_delta(req) {
                    Ok(delta) => delta,
                    Err(err) => {
                        info!("Error {:?}", err);
                        return Status::BadRequest
                    }
                };
                Action::Lattice {
                    namespace: namespace,
                    delta: delta,
                }
            }
        };
        self.chat_pool.send(action);
        Status::NoContent
    }

}

fn decode_delta(req: &Request) -> Result<Delta, json::DecoderError>
{
    // NOTE: we assume body exist and is valid json
    //  (ie: it parsed earlier)
    let body = req.body.as_ref().unwrap();
    let body = str::from_utf8(&body.data[..]).unwrap();
    json::decode::<Delta>(body)
}

quick_error! {
    #[derive(Debug)]
    enum MatchError {
        BadIdent(err: BadIdent) {
            from()
            display("bad ident: {}", err)
            description(err.description())
        }
        UnknownRoute {
            description("unknown route")
        }
        InvalidRoute {
            description("malformed route")
        }
    }
}

fn match_route(method: &Method, path: &str) -> Result<ChatRoute, MatchError> {
    use minihttp::enums::Method::*;
    use self::ChatRoute::*;
    use self::MatchError::*;

    if !path.starts_with("/v1/") {
        return Err(UnknownRoute)
    }
    let mut it = path.split("/").skip(2);   // skip '' & 'v1'
    let route = match (method, it.next()) {
        (&Post, Some(kind)) => {
            if !it.next().map(|x| x.len() > 0).unwrap_or(false) {
                return Err(InvalidRoute)
            }
            match kind {
                "lattice" => {
                    let (_, namespace) = path.split_at("/v1/lattice/".len());
                    let namespace = namespace.replace("/", ".").parse()?;
                    ChatRoute::LatticeUpdate(namespace)
                }
                "publish" => {
                    let (_, topic) = path.split_at("/v1/publish/".len());
                    let topic = topic.replace("/", ".").parse()?;
                    TopicPublish(topic)
                }
                _ => return Err(UnknownRoute)
            }
        }
        (&Put, Some("connection")) |
        (&Delete, Some("connection")) => {
            let id = match it.next() {
                Some(id) if id.len() > 0 => id,
                _ => return Err(InvalidRoute),
            };
            // TODO: validate ConnectionID (/ClientID)

            let kind = it.next();
            if !it.next().map(|x| x.len() > 0).unwrap_or(false) {
                return Err(InvalidRoute)
            }
            match kind {
                Some("subscriptions") => {
                    let (_, topic) = path.split_at(
                        "/v1/connection//subscriptions/".len() + id.len());
                    let topic = topic.replace("/", ".").parse()?;
                    if method == &Put {
                        TopicSubscribe(id.to_string(), topic)
                    } else {
                        TopicUnsubscribe(id.to_string(), topic)
                    }
                }
                Some("lattices") => {
                    let (_, namespace) = path.split_at(
                        "/v1/connection//lattices/".len() + id.len());
                    let namespace = namespace.replace("/", ".").parse()?;
                    if method == &Put {
                        LatticeSubscribe(
                            id.to_string(), namespace)
                    } else {
                        LatticeUnsubscribe(
                            id.to_string(), namespace)
                    }
                }
                _ => return Err(UnknownRoute)
            }
        }
        _ => return Err(UnknownRoute)
    };
    Ok(route)
}

#[derive(Debug, PartialEq)]
pub enum ChatRoute {
    TopicSubscribe(String, Topic),
    TopicUnsubscribe(String, Topic),
    LatticeSubscribe(String, Lattice),
    LatticeUnsubscribe(String, Lattice),
    TopicPublish(Topic),
    LatticeUpdate(Lattice),
}

impl ChatRoute {
    fn expect_data(&self) -> bool {
        use self::ChatRoute::*;
        match *self {
            TopicPublish(_) |
            LatticeSubscribe(_, _) |
            LatticeUpdate(_) => true,

            TopicSubscribe(_, _) |
            TopicUnsubscribe(_, _) |
            LatticeUnsubscribe(_, _) => false,
        }
    }
}

#[cfg(test)]
mod test {
    use minihttp::enums::Method;
    use string_intern::Symbol;

    use super::match_route;
    use super::ChatRoute::*;
    use super::MatchError;

    #[test]
    fn match_topic_publish() {
        let path = "/v1/publish/test-chat/room1";
        let route = match_route(&Method::Post, path).unwrap();
        assert_eq!(route, TopicPublish(Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_update() {
        let path = "/v1/lattice/test-chat/rooms";
        let route = match_route(&Method::Post, path).unwrap();
        assert_eq!(route, LatticeUpdate(Symbol::from("test-chat.rooms")));
    }

    #[test]
    fn match_topic_subscribe() {
        let path = "/v1/connection/abcde/subscriptions/test-chat/room1";
        let route = match_route(&Method::Put, path).unwrap();
        assert_eq!(route, TopicSubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_topic_unsubscribe() {
        let path = "/v1/connection/abcde/subscriptions/test-chat/room1";
        let route = match_route(&Method::Delete, path).unwrap();
        assert_eq!(route, TopicUnsubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_subscribe() {
        let path = "/v1/connection/abcde/lattices/test-chat/room1";
        let route = match_route(&Method::Put, path).unwrap();
        assert_eq!(route, LatticeSubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn match_lattice_unsubscribe() {
        let path = "/v1/connection/abcde/lattices/test-chat/room1";
        let route = match_route(&Method::Delete, path).unwrap();
        assert_eq!(route, LatticeUnsubscribe(
            "abcde".to_string(), Symbol::from("test-chat.room1")));
    }

    #[test]
    fn no_matches() {
        let path = "/v/";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/publish";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/publish/";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/publish/test-chat/room1";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/lattice/test-chat/rooms";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/connection//subscriptions/";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/connection/abc/subscriptions/";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());

        let path = "/v1/connection/conn_id/subscriptions/room,1";
        assert!(match_route(&Method::Get, path).is_err());
        assert!(match_route(&Method::Put, path).is_err());
        assert!(match_route(&Method::Post, path).is_err());
        assert!(match_route(&Method::Patch, path).is_err());
        assert!(match_route(&Method::Delete, path).is_err());
    }
}
<|endoftext|>