<commit_before>//! Standard symbolic constants and types
//!
use {Error, Result, NixPath, from_ffi};
use errno::Errno;
use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC};
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
use libc::{c_char, c_void, c_int, size_t, pid_t, off_t};
use std::mem;
use std::ffi::CString;
use std::os::unix::io::RawFd;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int, size_t};
    pub use libc::{close, read, write, pipe, ftruncate, unlink};
    pub use libc::funcs::posix88::unistd::{fork, getpid, getppid};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // Execute PATH with arguments ARGV and environment from `environ'.
        // doc: http://man7.org/linux/man-pages/man3/execv.3.html
        pub fn execv (path: *const c_char, argv: *const *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(path: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // Execute FILE, searching in the `PATH' environment variable if it contains
        // no slashes, with arguments ARGV and environment from `environ'.
        // doc: http://man7.org/linux/man-pages/man3/execvp.3.html
        pub fn execvp(filename: *const c_char, argv: *const *const c_char) -> c_int;

        // doc: http://man7.org/linux/man-pages/man3/exec.3.html
        #[cfg(any(target_os = "linux", target_os = "android"))]
        #[cfg(feature = "execvpe")]
        pub fn execvpe(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;

        // sets the hostname to the value given
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn gethostname(name: *mut c_char, len: size_t) -> c_int;

        // gets the hostname
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn sethostname(name: *const c_char, len: size_t) -> c_int;

        // change root directory
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn chroot(path: *const c_char) -> c_int;
    }
}

#[derive(Clone, Copy)]
pub enum Fork {
    Parent(pid_t),
    Child
}

impl Fork {
    pub fn is_child(&self) -> bool {
        match *self {
            Fork::Child => true,
            _ => false
        }
    }

    pub fn is_parent(&self) -> bool {
        match *self {
            Fork::Parent(_) => true,
            _ => false
        }
    }
}

pub fn fork() -> Result<Fork> {
    use self::Fork::*;

    let res = unsafe { ffi::fork() };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    } else if res == 0 {
        Ok(Child)
    } else {
        Ok(Parent(res))
    }
}

#[inline]
pub fn getpid() -> pid_t {
    unsafe { ffi::getpid() } // no error handling, according to man page: "These functions are always successful."
}
#[inline]
pub fn getppid() -> pid_t {
    unsafe { ffi::getppid() } // no error handling, according to man page: "These functions are always successful."
}

#[inline]
pub fn dup(oldfd: RawFd) -> Result<RawFd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
    dup3_polyfill(oldfd, newfd, flags)
}

#[inline]
fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
    use errno::EINVAL;

    if oldfd == newfd {
        return Err(Error::Sys(Errno::EINVAL));
    }

    let fd = try!(dup2(oldfd, newfd));

    if flags.contains(O_CLOEXEC) {
        if let Err(e) = fcntl(fd, F_SETFD(FD_CLOEXEC)) {
            let _ = close(fd);
            return Err(e);
        }
    }

    Ok(fd)
}

#[inline]
pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { ffi::chdir(cstr.as_ptr()) }
    }));

    if res != 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(())
}

fn to_exec_array(args: &[CString]) -> *const *const c_char {
    use std::ptr;
    use libc::c_char;

    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());
    args_p.as_ptr()
}

#[inline]
pub fn execv(path: &CString, argv: &[CString]) -> Result<()> {
    let args_p = to_exec_array(argv);

    unsafe {
        ffi::execv(path.as_ptr(), args_p)
    };

    Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<()> {
    let args_p = to_exec_array(args);
    let env_p = to_exec_array(env);

    unsafe {
        ffi::execve(path.as_ptr(), args_p, env_p)
    };

    Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execvp(filename: &CString, args: &[CString]) -> Result<()> {
    let args_p = to_exec_array(args);

    unsafe {
        ffi::execvp(filename.as_ptr(), args_p)
    };

    Err(Error::Sys(Errno::last()))
}

pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn sethostname(name: &[u8]) -> Result<()> {
    let ptr = name.as_ptr() as *const c_char;
    let len = name.len() as size_t;

    let res = unsafe { ffi::sethostname(ptr, len) };
    from_ffi(res)
}

pub fn gethostname(name: &mut [u8]) -> Result<()> {
    let ptr = name.as_mut_ptr() as *mut c_char;
    let len = name.len() as size_t;

    let res = unsafe { ffi::gethostname(ptr, len) };
    from_ffi(res)
}

pub fn close(fd: RawFd) -> Result<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(res as usize)
}

pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(res as usize)
}

pub fn pipe() -> Result<(RawFd, RawFd)> {
    unsafe {
        let mut fds: [c_int; 2] = mem::uninitialized();

        let res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(Error::Sys(Errno::last()));
        }

        Ok((fds[0], fds[1]))
    }
}

pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
    unsafe {
        let mut fds: [c_int; 2] = mem::uninitialized();

        let res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(Error::Sys(Errno::last()));
        }

        try!(pipe2_setflags(fds[0], fds[1], flags));

        Ok((fds[0], fds[1]))
    }
}

fn pipe2_setflags(fd1: RawFd, fd2: RawFd, flags: OFlag) -> Result<()> {
    let mut res = Ok(0);

    if flags.contains(O_CLOEXEC) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFD(FD_CLOEXEC)))
            .and_then(|_| fcntl(fd2, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(O_NONBLOCK) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFL(O_NONBLOCK)))
            .and_then(|_| fcntl(fd2, F_SETFL(O_NONBLOCK)));
    }

    match res {
        Ok(_) => Ok(()),
        Err(e) => {
            let _ = close(fd1);
            let _ = close(fd2);
            return Err(e);
        }
    }
}

pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> {
    if unsafe { ffi::ftruncate(fd, len) } < 0 {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(())
    }
}

pub fn isatty(fd: RawFd) -> Result<bool> {
    use libc;

    if unsafe { libc::isatty(fd) } == 1 {
        Ok(true)
    } else {
        match Errno::last() {
            // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so
            // we return `Ok(false)`
            Errno::ENOTTY => Ok(false),
            err => Err(Error::Sys(err))
        }
    }
}

pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
    unsafe {
        ffi::unlink(cstr.as_ptr())
    }
    }));
    from_ffi(res)
}

#[inline]
pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { ffi::chroot(cstr.as_ptr()) }
    }));

    if res != 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(())
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux {
    use sys::syscall::{syscall, SYSPIVOTROOT};
    use errno::Errno;
    use {Error, Result, NixPath};

    #[cfg(feature = "execvpe")]
    use std::ffi::CString;

    pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
            new_root: &P1, put_old: &P2) -> Result<()> {
        let res = try!(try!(new_root.with_nix_path(|new_root| {
            put_old.with_nix_path(|put_old| {
                unsafe {
                    syscall(SYSPIVOTROOT, new_root.as_ptr(), put_old.as_ptr())
                }
            })
        })));

        if res != 0 {
            return Err(Error::Sys(Errno::last()));
        }

        Ok(())
    }

    #[inline]
    #[cfg(feature = "execvpe")]
    pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
        use std::ptr;
        use libc::c_char;

        let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
        args_p.push(ptr::null());

        let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
        env_p.push(ptr::null());

        unsafe {
            super::ffi::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
        };

        Err(Error::Sys(Errno::last()))
    }
}
<commit_msg>Fix memory issue with exec family of fns<commit_after>//! Standard symbolic constants and types
//!
use {Error, Result, NixPath, from_ffi};
use errno::Errno;
use fcntl::{fcntl, OFlag, O_NONBLOCK, O_CLOEXEC, FD_CLOEXEC};
use fcntl::FcntlArg::{F_SETFD, F_SETFL};
use libc::{c_char, c_void, c_int, size_t, pid_t, off_t};
use std::mem;
use std::ffi::CString;
use std::os::unix::io::RawFd;

#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int, size_t};
    pub use libc::{close, read, write, pipe, ftruncate, unlink};
    pub use libc::funcs::posix88::unistd::{fork, getpid, getppid};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // Execute PATH with arguments ARGV and environment from `environ'.
        // doc: http://man7.org/linux/man-pages/man3/execv.3.html
        pub fn execv (path: *const c_char, argv: *const *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(path: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // Execute FILE, searching in the `PATH' environment variable if it contains
        // no slashes, with arguments ARGV and environment from `environ'.
        // doc: http://man7.org/linux/man-pages/man3/execvp.3.html
        pub fn execvp(filename: *const c_char, argv: *const *const c_char) -> c_int;

        // doc: http://man7.org/linux/man-pages/man3/exec.3.html
        #[cfg(any(target_os = "linux", target_os = "android"))]
        #[cfg(feature = "execvpe")]
        pub fn execvpe(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;

        // sets the hostname to the value given
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn gethostname(name: *mut c_char, len: size_t) -> c_int;

        // gets the hostname
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn sethostname(name: *const c_char, len: size_t) -> c_int;

        // change root directory
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn chroot(path: *const c_char) -> c_int;
    }
}

#[derive(Clone, Copy)]
pub enum Fork {
    Parent(pid_t),
    Child
}

impl Fork {
    pub fn is_child(&self) -> bool {
        match *self {
            Fork::Child => true,
            _ => false
        }
    }

    pub fn is_parent(&self) -> bool {
        match *self {
            Fork::Parent(_) => true,
            _ => false
        }
    }
}

pub fn fork() -> Result<Fork> {
    use self::Fork::*;

    let res = unsafe { ffi::fork() };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    } else if res == 0 {
        Ok(Child)
    } else {
        Ok(Parent(res))
    }
}

#[inline]
pub fn getpid() -> pid_t {
    unsafe { ffi::getpid() } // no error handling, according to man page: "These functions are always successful."
}
#[inline]
pub fn getppid() -> pid_t {
    unsafe { ffi::getppid() } // no error handling, according to man page: "These functions are always successful."
}

#[inline]
pub fn dup(oldfd: RawFd) -> Result<RawFd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(res)
}

pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
    dup3_polyfill(oldfd, newfd, flags)
}

#[inline]
fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
    use errno::EINVAL;

    if oldfd == newfd {
        return Err(Error::Sys(Errno::EINVAL));
    }

    let fd = try!(dup2(oldfd, newfd));

    if flags.contains(O_CLOEXEC) {
        if let Err(e) = fcntl(fd, F_SETFD(FD_CLOEXEC)) {
            let _ = close(fd);
            return Err(e);
        }
    }

    Ok(fd)
}

#[inline]
pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { ffi::chdir(cstr.as_ptr()) }
    }));

    if res != 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(())
}

fn to_exec_array(args: &[CString]) -> Vec<*const c_char> {
    use std::ptr;
    use libc::c_char;

    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());
    args_p
}

#[inline]
pub fn execv(path: &CString, argv: &[CString]) -> Result<()> {
    let args_p = to_exec_array(argv);

    unsafe {
        ffi::execv(path.as_ptr(), args_p.as_ptr())
    };

    Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execve(path: &CString, args: &[CString], env: &[CString]) -> Result<()> {
    let args_p = to_exec_array(args);
    let env_p = to_exec_array(env);

    unsafe {
        ffi::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
    };

    Err(Error::Sys(Errno::last()))
}

#[inline]
pub fn execvp(filename: &CString, args: &[CString]) -> Result<()> {
    let args_p = to_exec_array(args);

    unsafe {
        ffi::execvp(filename.as_ptr(), args_p.as_ptr())
    };

    Err(Error::Sys(Errno::last()))
}

pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn sethostname(name: &[u8]) -> Result<()> {
    let ptr = name.as_ptr() as *const c_char;
    let len = name.len() as size_t;

    let res = unsafe { ffi::sethostname(ptr, len) };
    from_ffi(res)
}

pub fn gethostname(name: &mut [u8]) -> Result<()> {
    let ptr = name.as_mut_ptr() as *mut c_char;
    let len = name.len() as size_t;

    let res = unsafe { ffi::gethostname(ptr, len) };
    from_ffi(res)
}

pub fn close(fd: RawFd) -> Result<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(res as usize)
}

pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(Error::Sys(Errno::last()));
    }

    return Ok(res as usize)
}

pub fn pipe() -> Result<(RawFd, RawFd)> {
    unsafe {
        let mut fds: [c_int; 2] = mem::uninitialized();

        let res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(Error::Sys(Errno::last()));
        }

        Ok((fds[0], fds[1]))
    }
}

pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
    unsafe {
        let mut fds: [c_int; 2] = mem::uninitialized();

        let res = ffi::pipe(fds.as_mut_ptr());

        if res < 0 {
            return Err(Error::Sys(Errno::last()));
        }

        try!(pipe2_setflags(fds[0], fds[1], flags));

        Ok((fds[0], fds[1]))
    }
}

fn pipe2_setflags(fd1: RawFd, fd2: RawFd, flags: OFlag) -> Result<()> {
    let mut res = Ok(0);

    if flags.contains(O_CLOEXEC) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFD(FD_CLOEXEC)))
            .and_then(|_| fcntl(fd2, F_SETFD(FD_CLOEXEC)));
    }

    if flags.contains(O_NONBLOCK) {
        res = res
            .and_then(|_| fcntl(fd1, F_SETFL(O_NONBLOCK)))
            .and_then(|_| fcntl(fd2, F_SETFL(O_NONBLOCK)));
    }

    match res {
        Ok(_) => Ok(()),
        Err(e) => {
            let _ = close(fd1);
            let _ = close(fd2);
            return Err(e);
        }
    }
}

pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> {
    if unsafe { ffi::ftruncate(fd, len) } < 0 {
        Err(Error::Sys(Errno::last()))
    } else {
        Ok(())
    }
}

pub fn isatty(fd: RawFd) -> Result<bool> {
    use libc;

    if unsafe { libc::isatty(fd) } == 1 {
        Ok(true)
    } else {
        match Errno::last() {
            // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so
            // we return `Ok(false)`
            Errno::ENOTTY => Ok(false),
            err => Err(Error::Sys(err))
        }
    }
}

pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
    unsafe {
        ffi::unlink(cstr.as_ptr())
    }
    }));
    from_ffi(res)
}

#[inline]
pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
    let res = try!(path.with_nix_path(|cstr| {
        unsafe { ffi::chroot(cstr.as_ptr()) }
    }));

    if res != 0 {
        return Err(Error::Sys(Errno::last()));
    }

    Ok(())
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux {
    use sys::syscall::{syscall, SYSPIVOTROOT};
    use errno::Errno;
    use {Error, Result, NixPath};

    #[cfg(feature = "execvpe")]
    use std::ffi::CString;

    pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
            new_root: &P1, put_old: &P2) -> Result<()> {
        let res = try!(try!(new_root.with_nix_path(|new_root| {
            put_old.with_nix_path(|put_old| {
                unsafe {
                    syscall(SYSPIVOTROOT, new_root.as_ptr(), put_old.as_ptr())
                }
            })
        })));

        if res != 0 {
            return Err(Error::Sys(Errno::last()));
        }

        Ok(())
    }

    #[inline]
    #[cfg(feature = "execvpe")]
    pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<()> {
        use std::ptr;
        use libc::c_char;

        let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
        args_p.push(ptr::null());

        let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
        env_p.push(ptr::null());

        unsafe {
            super::ffi::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
        };

        Err(Error::Sys(Errno::last()))
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::c_str::{CString, ToCStr};
use libc::{c_char, c_void, c_int, size_t};
use fcntl::{Fd, OFlag};
use errno::{SysResult, SysError, from_ffi};

#[cfg(target_os = "linux")]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int};
    pub use libc::{close, read, write};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;
        pub fn dup3(oldfd: c_int, newfd: c_int, flags: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;
    }
}

#[inline]
pub fn dup(oldfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: Fd, newfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup3(oldfd: Fd, newfd: Fd, flags: OFlag) -> SysResult<Fd> {
    let res = unsafe { ffi::dup3(oldfd, newfd, flags.bits()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn chdir<S: ToCStr>(path: S) -> SysResult<()> {
    let path = path.to_c_str();
    let res = unsafe { ffi::chdir(path.as_ptr()) };

    if res != 0 {
        return Err(SysError::last());
    }

    return Ok(())
}

#[inline]
pub fn execve(filename: CString, args: &[CString], env: &[CString]) -> SysResult<()> {
    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());

    let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
    env_p.push(ptr::null());

    let res = unsafe {
        ffi::execve(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
    };

    if res != 0 {
        return Err(SysError::last());
    }

    // Should never reach here
    Ok(())
}

pub fn daemon(nochdir: bool, noclose: bool) -> SysResult<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn close(fd: Fd) -> SysResult<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: Fd, buf: &mut [u8]) -> SysResult<uint> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn write(fd: Fd, buf: &[u8]) -> SysResult<uint> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

#[cfg(target_os = "linux")]
mod linux {
    use std::path::Path;
    use syscall::{syscall, SysPivotRoot};
    use errno::{SysResult, SysError};

    pub fn pivot_root(new_root: &Path, put_old: &Path) -> SysResult<()> {
        let new_root = new_root.to_c_str();
        let put_old = put_old.to_c_str();

        let res = unsafe {
            syscall(SysPivotRoot, new_root.as_ptr(), put_old.as_ptr())
        };

        if res != 0 {
            return Err(SysError::last());
        }

        Ok(())
    }
}
<commit_msg>Add gethostname and sethostname<commit_after>use std::ptr;
use std::c_str::{CString, ToCStr};
use libc::{c_char, c_void, c_int, size_t};
use fcntl::{Fd, OFlag};
use errno::{SysResult, SysError, from_ffi};

#[cfg(target_os = "linux")]
pub use self::linux::*;

mod ffi {
    use libc::{c_char, c_int, size_t};
    pub use libc::{close, read, write};

    extern {
        // duplicate a file descriptor
        // doc: http://man7.org/linux/man-pages/man2/dup.2.html
        pub fn dup(oldfd: c_int) -> c_int;
        pub fn dup2(oldfd: c_int, newfd: c_int) -> c_int;
        pub fn dup3(oldfd: c_int, newfd: c_int, flags: c_int) -> c_int;

        // change working directory
        // doc: http://man7.org/linux/man-pages/man2/chdir.2.html
        pub fn chdir(path: *const c_char) -> c_int;

        // execute program
        // doc: http://man7.org/linux/man-pages/man2/execve.2.html
        pub fn execve(filename: *const c_char, argv: *const *const c_char, envp: *const *const c_char) -> c_int;

        // run the current process in the background
        // doc: http://man7.org/linux/man-pages/man3/daemon.3.html
        pub fn daemon(nochdir: c_int, noclose: c_int) -> c_int;

        // sets the hostname to the value given
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn gethostname(name: *mut c_char, len: size_t) -> c_int;

        // gets the hostname
        // doc: http://man7.org/linux/man-pages/man2/gethostname.2.html
        pub fn sethostname(name: *const c_char, len: size_t) -> c_int;
    }
}

#[inline]
pub fn dup(oldfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup(oldfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup2(oldfd: Fd, newfd: Fd) -> SysResult<Fd> {
    let res = unsafe { ffi::dup2(oldfd, newfd) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn dup3(oldfd: Fd, newfd: Fd, flags: OFlag) -> SysResult<Fd> {
    let res = unsafe { ffi::dup3(oldfd, newfd, flags.bits()) };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(res)
}

#[inline]
pub fn chdir<S: ToCStr>(path: S) -> SysResult<()> {
    let path = path.to_c_str();
    let res = unsafe { ffi::chdir(path.as_ptr()) };

    if res != 0 {
        return Err(SysError::last());
    }

    return Ok(())
}

#[inline]
pub fn execve(filename: CString, args: &[CString], env: &[CString]) -> SysResult<()> {
    let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect();
    args_p.push(ptr::null());

    let mut env_p: Vec<*const c_char> = env.iter().map(|s| s.as_ptr()).collect();
    env_p.push(ptr::null());

    let res = unsafe {
        ffi::execve(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
    };

    if res != 0 {
        return Err(SysError::last());
    }

    // Should never reach here
    Ok(())
}

pub fn daemon(nochdir: bool, noclose: bool) -> SysResult<()> {
    let res = unsafe { ffi::daemon(nochdir as c_int, noclose as c_int) };
    from_ffi(res)
}

pub fn sethostname(name: &[u8]) -> SysResult<()> {
    let ptr = name.as_ptr() as *const c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::sethostname(ptr, len) };
    from_ffi(res)
}

pub fn gethostname(name: &mut [u8]) -> SysResult<()> {
    let ptr = name.as_mut_ptr() as *mut c_char;
    let len = name.len() as u64;

    let res = unsafe { ffi::gethostname(ptr, len) };
    from_ffi(res)
}

pub fn close(fd: Fd) -> SysResult<()> {
    let res = unsafe { ffi::close(fd) };
    from_ffi(res)
}

pub fn read(fd: Fd, buf: &mut [u8]) -> SysResult<uint> {
    let res = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

pub fn write(fd: Fd, buf: &[u8]) -> SysResult<uint> {
    let res = unsafe { ffi::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t) };

    if res < 0 {
        return Err(SysError::last());
    }

    return Ok(res as uint)
}

#[cfg(target_os = "linux")]
mod linux {
    use std::path::Path;
    use syscall::{syscall, SysPivotRoot};
    use errno::{SysResult, SysError};

    pub fn pivot_root(new_root: &Path, put_old: &Path) -> SysResult<()> {
        let new_root = new_root.to_c_str();
        let put_old = put_old.to_c_str();

        let res = unsafe {
            syscall(SysPivotRoot, new_root.as_ptr(), put_old.as_ptr())
        };

        if res != 0 {
            return Err(SysError::last());
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::util::clean;
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::process::Command;

pub fn verify() -> Result<(), ()> {
    compile_only("exercises/variables/variables1.rs")?;
    compile_only("exercises/variables/variables2.rs")?;
    compile_only("exercises/variables/variables3.rs")?;
    compile_only("exercises/variables/variables4.rs")?;
    test("exercises/if/if1.rs")?;
    compile_only("exercises/functions/functions1.rs")?;
    compile_only("exercises/functions/functions2.rs")?;
    compile_only("exercises/functions/functions3.rs")?;
    compile_only("exercises/functions/functions4.rs")?;
    compile_only("exercises/functions/functions5.rs")?;
    test("exercises/test1.rs")?;
    compile_only("exercises/primitive_types/primitive_types1.rs")?;
    compile_only("exercises/primitive_types/primitive_types2.rs")?;
    compile_only("exercises/primitive_types/primitive_types3.rs")?;
    compile_only("exercises/primitive_types/primitive_types4.rs")?;
    compile_only("exercises/primitive_types/primitive_types5.rs")?;
    compile_only("exercises/primitive_types/primitive_types6.rs")?;
    test("exercises/tests/tests1.rs")?;
    test("exercises/tests/tests2.rs")?;
    test("exercises/tests/tests3.rs")?;
    test("exercises/test2.rs")?;
    compile_only("exercises/strings/strings1.rs")?;
    compile_only("exercises/strings/strings2.rs")?;
    compile_only("exercises/test3.rs")?;
    compile_only("exercises/modules/modules1.rs")?;
    compile_only("exercises/modules/modules2.rs")?;
    compile_only("exercises/macros/macros1.rs")?;
    compile_only("exercises/macros/macros2.rs")?;
    compile_only("exercises/macros/macros3.rs")?;
    compile_only("exercises/macros/macros4.rs")?;
    compile_only("exercises/test4.rs")?;
    compile_only("exercises/move_semantics/move_semantics1.rs")?;
    compile_only("exercises/move_semantics/move_semantics2.rs")?;
    compile_only("exercises/move_semantics/move_semantics3.rs")?;
    compile_only("exercises/move_semantics/move_semantics4.rs")?;
    test("exercises/error_handling/errors1.rs")?;
    test("exercises/error_handling/errors2.rs")?;
    test("exercises/error_handling/errors3.rs")?;
    test("exercises/error_handling/errorsn.rs")?;
    compile_only("exercises/error_handling/option1.rs")?;
    test("exercises/error_handling/result1.rs")?;
    Ok(())
}

fn compile_only(filename: &str) -> Result<(), ()> {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Compiling {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let compilecmd = Command::new("rustc")
        .args(&[filename, "-o", "temp", "--color", "always"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if compilecmd.status.success() {
        let formatstr = format!(
            "{} Successfully compiled {}!",
            Emoji("✅", "✓"),
            filename
        );
        println!("{}", style(formatstr).green());
        clean();
        Ok(())
    } else {
        let formatstr = format!(
            "{} Compilation of {} failed! Compiler error message:\n",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
        clean();
        Err(())
    }
}

pub fn test(filename: &str) -> Result<(), ()> {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Testing {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let testcmd = Command::new("rustc")
        .args(&["--test", filename, "-o", "temp", "--color", "always"])
        .output()
        .expect("fail");
    if testcmd.status.success() {
        bar.set_message(format!("Running {}...", filename).as_str());
        let runcmd = Command::new("./temp").output().expect("fail");
        bar.finish_and_clear();

        if runcmd.status.success() {
            let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename);
            println!("{}", style(formatstr).green());
            clean();
            Ok(())
        } else {
            let formatstr = format!(
                "{} Testing of {} failed! Please try again. Here's the output:",
                Emoji("⚠ ", "!"),
                filename
            );
            println!("{}", style(formatstr).red());
            println!("{}", String::from_utf8_lossy(&runcmd.stdout));
            clean();
            Err(())
        }
    } else {
        bar.finish_and_clear();
        let formatstr = format!(
            "{} Compiling of {} failed! Please try again. Here's the output:",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&testcmd.stderr));
        clean();
        Err(())
    }
}
<commit_msg>Add thread exercises to verify entrypoint<commit_after>use crate::util::clean;
use console::{style, Emoji};
use indicatif::ProgressBar;
use std::process::Command;

pub fn verify() -> Result<(), ()> {
    compile_only("exercises/variables/variables1.rs")?;
    compile_only("exercises/variables/variables2.rs")?;
    compile_only("exercises/variables/variables3.rs")?;
    compile_only("exercises/variables/variables4.rs")?;
    test("exercises/if/if1.rs")?;
    compile_only("exercises/functions/functions1.rs")?;
    compile_only("exercises/functions/functions2.rs")?;
    compile_only("exercises/functions/functions3.rs")?;
    compile_only("exercises/functions/functions4.rs")?;
    compile_only("exercises/functions/functions5.rs")?;
    test("exercises/test1.rs")?;
    compile_only("exercises/primitive_types/primitive_types1.rs")?;
    compile_only("exercises/primitive_types/primitive_types2.rs")?;
    compile_only("exercises/primitive_types/primitive_types3.rs")?;
    compile_only("exercises/primitive_types/primitive_types4.rs")?;
    compile_only("exercises/primitive_types/primitive_types5.rs")?;
    compile_only("exercises/primitive_types/primitive_types6.rs")?;
    test("exercises/tests/tests1.rs")?;
    test("exercises/tests/tests2.rs")?;
    test("exercises/tests/tests3.rs")?;
    test("exercises/test2.rs")?;
    compile_only("exercises/strings/strings1.rs")?;
    compile_only("exercises/strings/strings2.rs")?;
    compile_only("exercises/test3.rs")?;
    compile_only("exercises/modules/modules1.rs")?;
    compile_only("exercises/modules/modules2.rs")?;
    compile_only("exercises/macros/macros1.rs")?;
    compile_only("exercises/macros/macros2.rs")?;
    compile_only("exercises/macros/macros3.rs")?;
    compile_only("exercises/macros/macros4.rs")?;
    compile_only("exercises/test4.rs")?;
    compile_only("exercises/move_semantics/move_semantics1.rs")?;
    compile_only("exercises/move_semantics/move_semantics2.rs")?;
    compile_only("exercises/move_semantics/move_semantics3.rs")?;
    compile_only("exercises/move_semantics/move_semantics4.rs")?;
    test("exercises/error_handling/errors1.rs")?;
    test("exercises/error_handling/errors2.rs")?;
    test("exercises/error_handling/errors3.rs")?;
    test("exercises/error_handling/errorsn.rs")?;
    compile_only("exercises/error_handling/option1.rs")?;
    test("exercises/error_handling/result1.rs")?;
    compile_only("exercises/threads/threads1.rs")?;
    Ok(())
}

fn compile_only(filename: &str) -> Result<(), ()> {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Compiling {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let compilecmd = Command::new("rustc")
        .args(&[filename, "-o", "temp", "--color", "always"])
        .output()
        .expect("fail");
    bar.finish_and_clear();
    if compilecmd.status.success() {
        let formatstr = format!(
            "{} Successfully compiled {}!",
            Emoji("✅", "✓"),
            filename
        );
        println!("{}", style(formatstr).green());
        clean();
        Ok(())
    } else {
        let formatstr = format!(
            "{} Compilation of {} failed! Compiler error message:\n",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&compilecmd.stderr));
        clean();
        Err(())
    }
}

pub fn test(filename: &str) -> Result<(), ()> {
    let bar = ProgressBar::new_spinner();
    bar.set_message(format!("Testing {}...", filename).as_str());
    bar.enable_steady_tick(100);
    let testcmd = Command::new("rustc")
        .args(&["--test", filename, "-o", "temp", "--color", "always"])
        .output()
        .expect("fail");
    if testcmd.status.success() {
        bar.set_message(format!("Running {}...", filename).as_str());
        let runcmd = Command::new("./temp").output().expect("fail");
        bar.finish_and_clear();

        if runcmd.status.success() {
            let formatstr = format!("{} Successfully tested {}!", Emoji("✅", "✓"), filename);
            println!("{}", style(formatstr).green());
            clean();
            Ok(())
        } else {
            let formatstr = format!(
                "{} Testing of {} failed! Please try again. Here's the output:",
                Emoji("⚠ ", "!"),
                filename
            );
            println!("{}", style(formatstr).red());
            println!("{}", String::from_utf8_lossy(&runcmd.stdout));
            clean();
            Err(())
        }
    } else {
        bar.finish_and_clear();
        let formatstr = format!(
            "{} Compiling of {} failed! Please try again. Here's the output:",
            Emoji("⚠ ", "!"),
            filename
        );
        println!("{}", style(formatstr).red());
        println!("{}", String::from_utf8_lossy(&testcmd.stderr));
        clean();
        Err(())
    }
}
<|endoftext|>


<commit_before>use petgraph::{EdgeDirection, Graph};
use petgraph::graph::NodeIndex;
use crossbeam;

use ::compiler::{CommandInfo, CompilationTask, Compiler, OutputInfo, SharedState, Toolchain};

use std::io::{Error, ErrorKind};
use std::borrow::Cow;
use std::cmp::{max, min};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};

pub type BuildGraph = Graph<Arc<BuildTask>, ()>;

pub struct BuildTask {
    pub title: String,
    pub action: BuildAction,
}

pub enum BuildAction {
    Empty,
    Exec(CommandInfo, Vec<String>),
    Compilation(Arc<Toolchain>, CompilationTask),
}

pub struct BuildResult<'a> {
    // Completed task
    pub task: &'a BuildTask,
    // Worker number
    pub worker: usize,
    // Build result
    pub result: &'a Result<OutputInfo, Error>,
    // Completed task count
    pub completed: usize,
    // Total task count
    pub total: usize,
}

struct ResultMessage {
    index: NodeIndex,
    task: Arc<BuildTask>,
    worker: usize,
    result: Result<OutputInfo, Error>,
}

struct TaskMessage {
    index: NodeIndex,
    task: Arc<BuildTask>,
}

impl<'a> BuildResult<'a> {
    fn new(message: &'a ResultMessage, completed: &mut usize, total: usize) -> Self {
        *completed += 1;
        BuildResult {
            worker: message.worker,
            task: &message.task,
            result: &message.result,
            completed: *completed,
            total: total,
        }
    }
}

impl BuildAction {
    pub fn create_tasks<C: Compiler>(compiler: &C,
                                     command: CommandInfo,
                                     args: &[String],
                                     title: &str)
                                     -> Vec<BuildAction> {
        let actions: Vec<BuildAction> = compiler.create_tasks(command.clone(), &args)
            .map(|tasks| {
                tasks.into_iter()
                    .map(|(toolchain, task)| BuildAction::Compilation(toolchain, task))
                    .collect()
            })
            .unwrap_or_else(|e| {
                println!("Can't use octobuild for task {}: {}", title, e);
                Vec::new()
            });
        if actions.len() == 0 {
            return vec![BuildAction::Exec(command, args.to_vec())];
        }
        actions
    }

    pub fn title(self: &Self) -> Cow<str> {
        match self {
            &BuildAction::Empty => Cow::Borrowed(""),
            &BuildAction::Exec(_, ref args) => Cow::Owned(format!("{:?}", args)),
            &BuildAction::Compilation(_, ref task) => Cow::Borrowed(task.input_source.to_str().unwrap_or("")),
        }
    }
}

pub fn validate_graph<N, E>(graph: Graph<N, E>) -> Result<Graph<N, E>, Error> {
    let mut completed: Vec<bool> = Vec::with_capacity(graph.node_count());
    let mut queue: Vec<NodeIndex> = Vec::with_capacity(graph.node_count());
    for index in 0..graph.node_count() {
        completed.push(false);
        queue.push(NodeIndex::new(index));
    }
    let mut count: usize = 0;
    let mut i: usize = 0;
    while i < queue.len() {
        let index = queue[i];
        if (!completed[index.index()]) && (is_ready(&graph, &completed, &index)) {
            completed[index.index()] = true;
            for neighbor in graph.neighbors_directed(index, EdgeDirection::Incoming) {
                queue.push(neighbor);
            }
            count += 1;
            if count == completed.len() {
                return Ok(graph);
            }
        }
        i = i + 1;
    }
    Err(Error::new(ErrorKind::InvalidInput,
                   "Found cycles in build dependencies"))
}

fn execute_until_failed<F>(graph: &BuildGraph,
                           tx_task: Sender<TaskMessage>,
                           rx_result: &Receiver<ResultMessage>,
                           count: &mut usize,
                           update_progress: F)
                           -> Result<Option<i32>, Error>
    where F: Fn(BuildResult) -> Result<(), Error>
{
    let mut completed: Vec<bool> = Vec::new();
    for _ in 0..graph.node_count() {
        completed.push(false);
    }
    for index in graph.externals(EdgeDirection::Outgoing) {
        try!(tx_task.send(TaskMessage {
                index: index,
                task: graph.node_weight(index).unwrap().clone(),
            })
            .map_err(|e| Error::new(ErrorKind::Other, e)));
    }

    for message in rx_result.iter() {
        assert!(!completed[message.index.index()]);

        try!(update_progress(BuildResult::new(&message, count, graph.node_count())));
        let result = try!(message.result);
        if !result.success() {
            let status = result.status;
            return Ok(status);
        }
        completed[message.index.index()] = true;

        for source in graph.neighbors_directed(message.index, EdgeDirection::Incoming) {
            if is_ready(graph, &completed, &source) {
                try!(tx_task.send(TaskMessage {
                        index: source,
                        task: graph.node_weight(source).unwrap().clone(),
                    })
                    .map_err(|e| Error::new(ErrorKind::Other, e)));
            }
        }

        if *count == completed.len() {
            return Ok(Some(0));
        }
    }
    panic!("Unexpected end of result pipe");
}

fn is_ready<N, E>(graph: &Graph<N, E>, completed: &Vec<bool>, source: &NodeIndex) -> bool {
    for neighbor in graph.neighbors_directed(*source, EdgeDirection::Outgoing) {
        if !completed[neighbor.index()] {
            return false;
        }
    }
    true
}

pub fn execute_graph<F>(state: &SharedState,
                        build_graph: BuildGraph,
                        process_limit: usize,
                        update_progress: F)
                        -> Result<Option<i32>, Error>
    where F: Fn(BuildResult) -> Result<(), Error>
{
    let graph = try!(validate_graph(build_graph));
    if graph.node_count() == 0 {
        return Ok(Some(0));
    }

    if graph.node_count() == 1 {
        let task = &graph.raw_nodes()[0].weight;
        let result = execute_compiler(&state, task);
        try!(update_progress(BuildResult {
            worker: 0,
            completed: 1,
            total: 1,
            result: &result,
            task: task,
        }));
        return result.map(|output| output.status);
    }

    let (tx_result, rx_result): (Sender<ResultMessage>, Receiver<ResultMessage>) = channel();
    let (tx_task, rx_task): (Sender<TaskMessage>, Receiver<TaskMessage>) = channel();
    let mutex_rx_task = Arc::new(Mutex::new(rx_task));
    let num_cpus = max(1, min(process_limit, graph.node_count()));
    crossbeam::scope(|scope| {
        for worker_id in 0..num_cpus {
            let local_rx_task = mutex_rx_task.clone();
            let local_tx_result = tx_result.clone();
            scope.spawn(move || {
                loop {
                    let message = match local_rx_task.lock().unwrap().recv() {
                        Ok(v) => v,
                        Err(_) => {
                            break;
                        }
                    };
                    match local_tx_result.send(ResultMessage {
                        index: message.index,
                        worker: worker_id,
                        result: execute_compiler(state, &message.task),
                        task: message.task,
                    }) {
                        Ok(_) => {}
                        Err(_) => {
                            break;
                        }
                    }
                }
            });
        }
        // Run all tasks.
        let mut count: usize = 0;
        let result = execute_until_failed(&graph, tx_task, &rx_result, &mut count, &update_progress);
        // Cleanup task queue.
        for _ in mutex_rx_task.lock().unwrap().iter() {}
        // Wait for in progress task completion.
        for message in rx_result.iter() {
            try!(update_progress(BuildResult::new(&message, &mut count, graph.node_count())));
        }
        result
    })
}

fn execute_compiler(state: &SharedState, task: &BuildTask) -> Result<OutputInfo, Error> {
    match &task.action {
        &BuildAction::Empty => {
            Ok(OutputInfo {
                status: Some(0),
                stderr: Vec::new(),
                stdout: Vec::new(),
            })
        }
        &BuildAction::Exec(ref command, ref args) => {
            state.wrap_slow(|| command.to_command().args(args).output().map(|o| OutputInfo::new(o)))
        }
        &BuildAction::Compilation(ref toolchain, ref task) => toolchain.compile_task(state, task.clone()),
    }
}
<commit_msg>Fix xgConsole hangs on > 1 task (fixed #19)<commit_after>use petgraph::{EdgeDirection, Graph};
use petgraph::graph::NodeIndex;
use crossbeam;

use ::compiler::{CommandInfo, CompilationTask, Compiler, OutputInfo, SharedState, Toolchain};

use std::io::{Error, ErrorKind};
use std::borrow::Cow;
use std::cmp::{max, min};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{Receiver, Sender, channel};

pub type BuildGraph = Graph<Arc<BuildTask>, ()>;

pub struct BuildTask {
    pub title: String,
    pub action: BuildAction,
}

pub enum BuildAction {
    Empty,
    Exec(CommandInfo, Vec<String>),
    Compilation(Arc<Toolchain>, CompilationTask),
}

pub struct BuildResult<'a> {
    // Completed task
    pub task: &'a BuildTask,
    // Worker number
    pub worker: usize,
    // Build result
    pub result: &'a Result<OutputInfo, Error>,
    // Completed task count
    pub completed: usize,
    // Total task count
    pub total: usize,
}

struct ResultMessage {
    index: NodeIndex,
    task: Arc<BuildTask>,
    worker: usize,
    result: Result<OutputInfo, Error>,
}

struct TaskMessage {
    index: NodeIndex,
    task: Arc<BuildTask>,
}

impl<'a> BuildResult<'a> {
    fn new(message: &'a ResultMessage, completed: &mut usize, total: usize) -> Self {
        *completed += 1;
        BuildResult {
            worker: message.worker,
            task: &message.task,
            result: &message.result,
            completed: *completed,
            total: total,
        }
    }
}

impl BuildAction {
    pub fn create_tasks<C: Compiler>(compiler: &C,
                                     command: CommandInfo,
                                     args: &[String],
                                     title: &str)
                                     -> Vec<BuildAction> {
        let actions: Vec<BuildAction> = compiler.create_tasks(command.clone(), &args)
            .map(|tasks| {
                tasks.into_iter()
                    .map(|(toolchain, task)| BuildAction::Compilation(toolchain, task))
                    .collect()
            })
            .unwrap_or_else(|e| {
                println!("Can't use octobuild for task {}: {}", title, e);
                Vec::new()
            });
        if actions.len() == 0 {
            return vec![BuildAction::Exec(command, args.to_vec())];
        }
        actions
    }

    pub fn title(self: &Self) -> Cow<str> {
        match self {
            &BuildAction::Empty => Cow::Borrowed(""),
            &BuildAction::Exec(_, ref args) => Cow::Owned(format!("{:?}", args)),
            &BuildAction::Compilation(_, ref task) => Cow::Borrowed(task.input_source.to_str().unwrap_or("")),
        }
    }
}

pub fn validate_graph<N, E>(graph: Graph<N, E>) -> Result<Graph<N, E>, Error> {
    let mut completed: Vec<bool> = Vec::with_capacity(graph.node_count());
    let mut queue: Vec<NodeIndex> = Vec::with_capacity(graph.node_count());
    if graph.node_count() == 0 {
        return Ok(graph);
    }
    for index in 0..graph.node_count() {
        completed.push(false);
        queue.push(NodeIndex::new(index));
    }
    let mut count: usize = 0;
    let mut i: usize = 0;
    while i < queue.len() {
        let index = queue[i];
        if (!completed[index.index()]) && (is_ready(&graph, &completed, &index)) {
            completed[index.index()] = true;
            for neighbor in graph.neighbors_directed(index, EdgeDirection::Incoming) {
                queue.push(neighbor);
            }
            count += 1;
            if count == completed.len() {
                return Ok(graph);
            }
        }
        i = i + 1;
    }
    Err(Error::new(ErrorKind::InvalidInput,
                   "Found cycles in build dependencies"))
}

fn execute_until_failed<F>(graph: &BuildGraph,
                           tx_task: Sender<TaskMessage>,
                           rx_result: &Receiver<ResultMessage>,
                           count: &mut usize,
                           update_progress: F)
                           -> Result<Option<i32>, Error>
    where F: Fn(BuildResult) -> Result<(), Error>
{
    let mut completed: Vec<bool> = Vec::new();
    for _ in 0..graph.node_count() {
        completed.push(false);
    }
    for index in graph.externals(EdgeDirection::Outgoing) {
        try!(tx_task.send(TaskMessage {
                index: index,
                task: graph.node_weight(index).unwrap().clone(),
            })
            .map_err(|e| Error::new(ErrorKind::Other, e)));
    }

    for message in rx_result.iter() {
        assert!(!completed[message.index.index()]);

        try!(update_progress(BuildResult::new(&message, count, graph.node_count())));
        let result = try!(message.result);
        if !result.success() {
            let status = result.status;
            return Ok(status);
        }
        completed[message.index.index()] = true;

        for source in graph.neighbors_directed(message.index, EdgeDirection::Incoming) {
            if is_ready(graph, &completed, &source) {
                try!(tx_task.send(TaskMessage {
                        index: source,
                        task: graph.node_weight(source).unwrap().clone(),
                    })
                    .map_err(|e| Error::new(ErrorKind::Other, e)));
            }
        }

        if *count == completed.len() {
            return Ok(Some(0));
        }
    }
    panic!("Unexpected end of result pipe");
}

fn is_ready<N, E>(graph: &Graph<N, E>, completed: &Vec<bool>, source: &NodeIndex) -> bool {
    for neighbor in graph.neighbors_directed(*source, EdgeDirection::Outgoing) {
        if !completed[neighbor.index()] {
            return false;
        }
    }
    true
}

pub fn execute_graph<F>(state: &SharedState,
                        build_graph: BuildGraph,
                        process_limit: usize,
                        update_progress: F)
                        -> Result<Option<i32>, Error>
    where F: Fn(BuildResult) -> Result<(), Error>
{
    let graph = try!(validate_graph(build_graph));
    if graph.node_count() == 0 {
        return Ok(Some(0));
    }

    if graph.node_count() == 1 {
        let task = &graph.raw_nodes()[0].weight;
        let result = execute_compiler(&state, task);
        try!(update_progress(BuildResult {
            worker: 0,
            completed: 1,
            total: 1,
            result: &result,
            task: task,
        }));
        return result.map(|output| output.status);
    }

    let (tx_result, rx_result): (Sender<ResultMessage>, Receiver<ResultMessage>) = channel();
    let (tx_task, rx_task): (Sender<TaskMessage>, Receiver<TaskMessage>) = channel();
    let mutex_rx_task = Arc::new(Mutex::new(rx_task));
    let num_cpus = max(1, min(process_limit, graph.node_count()));
    crossbeam::scope(|scope| {
        for worker_id in 0..num_cpus {
            let local_rx_task = mutex_rx_task.clone();
            let local_tx_result = tx_result.clone();
            scope.spawn(move || {
                loop {
                    let message = match local_rx_task.lock().unwrap().recv() {
                        Ok(v) => v,
                        Err(_) => {
                            break;
                        }
                    };
                    match local_tx_result.send(ResultMessage {
                        index: message.index,
                        worker: worker_id,
                        result: execute_compiler(state, &message.task),
                        task: message.task,
                    }) {
                        Ok(_) => {}
                        Err(_) => {
                            break;
                        }
                    }
                }
            });
        }
        drop(tx_result);
        // Run all tasks.
        let mut count: usize = 0;
        let result = execute_until_failed(&graph, tx_task, &rx_result, &mut count, &update_progress);
        // Cleanup task queue.
        for _ in mutex_rx_task.lock().unwrap().iter() {}
        // Wait for in progress task completion.
        for message in rx_result.iter() {
            try!(update_progress(BuildResult::new(&message, &mut count, graph.node_count())));
        }
        result
    })
}

fn execute_compiler(state: &SharedState, task: &BuildTask) -> Result<OutputInfo, Error> {
    match &task.action {
        &BuildAction::Empty => {
            Ok(OutputInfo {
                status: Some(0),
                stderr: Vec::new(),
                stdout: Vec::new(),
            })
        }
        &BuildAction::Exec(ref command, ref args) => {
            state.wrap_slow(|| command.to_command().args(args).output().map(|o| OutputInfo::new(o)))
        }
        &BuildAction::Compilation(ref toolchain, ref task) => toolchain.compile_task(state, task.clone()),
    }
}

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

    use ::compiler::SharedState;
    use ::config::Config;

    use std::sync::{Arc, Mutex};

    #[test]
    fn test_execute_graph_empty() {
        let state = SharedState::new(&Config::defaults().unwrap());
        let graph = BuildGraph::new();
        execute_graph(&state, graph, 2, |_| {
                assert!(false);
                Ok(())
            })
            .unwrap();
    }

    #[test]
    fn test_execute_graph_single() {
        let state = SharedState::new(&Config::defaults().unwrap());

        // Simple two tasks graph
        let mut graph = BuildGraph::new();
        graph.add_node(Arc::new(BuildTask {
            title: "task 1".to_string(),
            action: BuildAction::Empty,
        }));

        let result = Mutex::new(Vec::new());
        execute_graph(&state, graph, 4, |r| {
                result.lock().unwrap().push(r.task.title.clone());
                Ok(())
            })
            .unwrap();

        let actual: Vec<String> = result.lock().unwrap().clone();
        assert_eq!(actual, vec!["task 1".to_string()]);
    }
    // Test for #19 issue (https://github.com/bozaro/octobuild/issues/19)
    #[test]
    fn test_execute_graph_no_hang() {
        let state = SharedState::new(&Config::defaults().unwrap());

        // Simple two tasks graph
        let mut graph = BuildGraph::new();
        let t1 = graph.add_node(Arc::new(BuildTask {
            title: "task 1".to_string(),
            action: BuildAction::Empty,
        }));
        let t2 = graph.add_node(Arc::new(BuildTask {
            title: "task 2".to_string(),
            action: BuildAction::Empty,
        }));
        graph.add_edge(t2, t1, ());

        let result = Mutex::new(Vec::new());
        execute_graph(&state, graph, 4, |r| {
                result.lock().unwrap().push(r.task.title.clone());
                Ok(())
            })
            .unwrap();

        let actual: Vec<String> = result.lock().unwrap().clone();
        assert_eq!(actual, vec!["task 1".to_string(), "task 2".to_string()]);
    }
}
<|endoftext|>


<commit_before>use std::fs::OpenOptions;
use std::io::Result;
use std::path::Path;

use byteorder::{ BigEndian, LittleEndian, WriteBytesExt };

pub fn write_pcm(filename: &str, samples: Vec<i16>) -> Result<()> {
    let path = Path::new(filename);
    let mut f = match OpenOptions::new().write(true).truncate(true).open(&path) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    for &sample in samples.iter() {
        try!(f.write_i16::<LittleEndian>(sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
pub fn write_wav(filename: &str, sample_rate: usize, samples: Vec<i16>) -> Result<()> {
    let path = Path::new(filename);
    let mut f = match OpenOptions::new().write(true).truncate(true).open(&path) {
        Ok(f) => f,
        Err(e) => panic!("File error: {}", e)
    };

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_i32::<BigEndian>(0x52494646));                // ChunkID, RIFF
    try!(f.write_i32::<LittleEndian>(chunk_size));             // ChunkSize
    try!(f.write_i32::<BigEndian>(0x57415645));                // Format, WAVE

    try!(f.write_i32::<BigEndian>(0x666d7420));                // Subchunk1ID, fmt
    try!(f.write_i32::<LittleEndian>(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_i16::<LittleEndian>(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_i16::<LittleEndian>(channels as i16));        // NumChannels
    try!(f.write_i32::<LittleEndian>(sample_rate as i32));     // SampleRate
    try!(f.write_i32::<LittleEndian>(byte_rate));              // ByteRate
    try!(f.write_i16::<LittleEndian>(block_align));            // BlockAlign
    try!(f.write_i16::<LittleEndian>(bit_depth as i16));       // BitsPerSample

    try!(f.write_i32::<BigEndian>(0x64617461));                // Subchunk2ID, data
    try!(f.write_i32::<LittleEndian>(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_i16::<LittleEndian>(*sample))
    }

    Ok(())
}
<commit_msg>writer fixes: OptionOptions.create should be true<commit_after>use std::fs::OpenOptions;
use std::io::Result;
use std::path::Path;

use byteorder::{ BigEndian, LittleEndian, WriteBytesExt };

pub fn write_pcm(filename: &str, samples: Vec<i16>) -> Result<()> {
    let path = Path::new(filename);
    let mut f = try!(OpenOptions::new().write(true).truncate(true).create(true).open(&path));

    for &sample in samples.iter() {
        try!(f.write_i16::<LittleEndian>(sample));
    }

    Ok(())
}

// See: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
pub fn write_wav(filename: &str, sample_rate: usize, samples: Vec<i16>) -> Result<()> {
    let path = Path::new(filename);
    let mut f = try!(OpenOptions::new().write(true).truncate(true).create(true).open(&path));

    // Some WAV header fields
    let channels = 1;
    let bit_depth = 16;
    let subchunk_2_size = samples.len() * channels * bit_depth / 8;
    let chunk_size = 36 + subchunk_2_size as i32;
    let byte_rate = (sample_rate * channels * bit_depth / 8) as i32;
    let block_align = (channels * bit_depth / 8) as i16;

    try!(f.write_i32::<BigEndian>(0x52494646));                // ChunkID, RIFF
    try!(f.write_i32::<LittleEndian>(chunk_size));             // ChunkSize
    try!(f.write_i32::<BigEndian>(0x57415645));                // Format, WAVE

    try!(f.write_i32::<BigEndian>(0x666d7420));                // Subchunk1ID, fmt
    try!(f.write_i32::<LittleEndian>(16));                     // Subchunk1Size, 16 for PCM
    try!(f.write_i16::<LittleEndian>(1));                      // AudioFormat, PCM = 1 (linear quantization)
    try!(f.write_i16::<LittleEndian>(channels as i16));        // NumChannels
    try!(f.write_i32::<LittleEndian>(sample_rate as i32));     // SampleRate
    try!(f.write_i32::<LittleEndian>(byte_rate));              // ByteRate
    try!(f.write_i16::<LittleEndian>(block_align));            // BlockAlign
    try!(f.write_i16::<LittleEndian>(bit_depth as i16));       // BitsPerSample

    try!(f.write_i32::<BigEndian>(0x64617461));                // Subchunk2ID, data
    try!(f.write_i32::<LittleEndian>(subchunk_2_size as i32)); // Subchunk2Size, number of bytes in the data

    for sample in samples.iter() {
        try!(f.write_i16::<LittleEndian>(*sample))
    }

    Ok(())
}
<|endoftext|>


<commit_before><commit_msg>Fix doc comment for libss_version()<commit_after><|endoftext|>


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

use serde_json::{self,
                 json,
                 Value as Json};
use std::{fs::{create_dir_all,
               read_to_string,
               File},
          io::Write,
          path::Path};
use toml::Value;

use crate::{common::{templating::TemplateRenderer,
                     ui::{Status,
                          UIWriter,
                          UI}},
            error::Result};

pub fn start(ui: &mut UI,
             template_path: &Path,
             default_toml_path: &Path,
             user_toml_path: Option<&Path>,
             mock_data_path: Option<&Path>,
             print: bool,
             render: bool,
             render_dir: &Path,
             quiet: bool)
             -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = match Path::new(&template_path).file_name() {
        Some(name) => Path::new(name),
        None => {
            panic!(format!("Something went wrong getting filename of {:?}",
                           &template_path))
        }
    };

    if !quiet {
        ui.begin(format!("Rendering: {} into: {} as: {}",
                         template_path.display(), render_dir.display(), file_name.display()))?;
        ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !quiet {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {}", &default_toml_path.display()))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml)?);

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing user.toml: {}", path.display()))?;
            }
            read_to_string(path)?
        }
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml)?);

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {}", path.display()))?;
            }
            read_to_string(path)?
        }
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data)?);

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer.register_template_string(&template, &template)
            .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data)?;

    if print {
        if !quiet {
            ui.br()?;
            ui.warn(format!("###======== Rendered template: {}", &template_path.display()))?;
        }

        println!("{}", rendered_template);

        if !quiet {
            ui.warn(format!("========### End rendered template: {}", &template_path.display()))?;
        }
    }

    if render {
        // Render our template file
        create_with_template(ui, &render_dir, &file_name, &rendered_template, quiet)?;
    }

    if !quiet {
        ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Result<Json> {
    let toml_value = cfg.parse::<Value>()?;
    let toml_string = serde_json::to_string(&toml_value)?;
    let json = serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string))?;
    Ok(json)
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    if let Json::Object(a_map) = a {
        if let Json::Object(b_map) = b {
            for (k, v) in b_map {
                merge(a_map.entry(k).or_insert(Json::Null), v);
            }
            return;
        }
    }
    *a = b;
}

fn create_with_template(ui: &mut UI,
                        render_dir: &std::path::Path,
                        file_name: &std::path::Path,
                        template: &str,
                        quiet: bool)
                        -> Result<()> {
    let path = Path::new(&render_dir).join(&file_name);
    if !quiet {
        ui.status(Status::Creating, format!("file: {}", path.display()))?;
    }

    create_dir_all(render_dir)?;

    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<commit_msg>simplify getting file_name<commit_after>// Copyright (c) 2019 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use serde_json::{self,
                 json,
                 Value as Json};
use std::{fs::{create_dir_all,
               read_to_string,
               File},
          io::Write,
          path::Path};
use toml::Value;

use crate::{common::{templating::TemplateRenderer,
                     ui::{Status,
                          UIWriter,
                          UI}},
            error::Result};

pub fn start(ui: &mut UI,
             template_path: &Path,
             default_toml_path: &Path,
             user_toml_path: Option<&Path>,
             mock_data_path: Option<&Path>,
             print: bool,
             render: bool,
             render_dir: &Path,
             quiet: bool)
             -> Result<()> {
    // Strip the file name out of our passed template
    let file_name = Path::new(template_path.file_name().expect("valid template file"));

    if !quiet {
        ui.begin(format!("Rendering: {} into: {} as: {}",
                         template_path.display(), render_dir.display(), file_name.display()))?;
        ui.br()?;
    }

    // read our template from file
    let template = read_to_string(&template_path)?;

    // create a "data" json struct
    let mut data = json!({});

    if !quiet {
        // import default.toml values, convert to JSON
        ui.begin(format!("Importing default.toml: {}", &default_toml_path.display()))?;
    }

    // we should always have a default.toml, would be nice to "autodiscover" based on package name,
    // for now assume we're working in the plan dir if --default-toml not passed
    let default_toml = read_to_string(&default_toml_path)?;

    // merge default into data struct
    merge(&mut data, toml_to_json(&default_toml)?);

    // import default.toml values, convert to JSON
    let user_toml = match user_toml_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing user.toml: {}", path.display()))?;
            }
            read_to_string(path)?
        }
        None => String::new(),
    };
    // merge default into data struct
    merge(&mut data, toml_to_json(&user_toml)?);

    // read mock data if provided
    let mock_data = match mock_data_path {
        Some(path) => {
            if !quiet {
                // print helper message, maybe only print if '--verbose'? how?
                ui.begin(format!("Importing override file: {}", path.display()))?;
            }
            read_to_string(path)?
        }
        // return an empty json block if '--mock-data' isn't defined.
        // this allows us to merge an empty JSON block
        None => "{}".to_string(),
    };
    // merge mock data into data
    merge(&mut data, serde_json::from_str(&mock_data)?);

    // create a template renderer
    let mut renderer = TemplateRenderer::new();
    // register our template
    renderer.register_template_string(&template, &template)
            .expect("Could not register template content");
    // render our JSON override in our template.
    let rendered_template = renderer.render(&template, &data)?;

    if print {
        if !quiet {
            ui.br()?;
            ui.warn(format!("###======== Rendered template: {}", &template_path.display()))?;
        }

        println!("{}", rendered_template);

        if !quiet {
            ui.warn(format!("========### End rendered template: {}", &template_path.display()))?;
        }
    }

    if render {
        // Render our template file
        create_with_template(ui, &render_dir, &file_name, &rendered_template, quiet)?;
    }

    if !quiet {
        ui.br()?;
    }
    Ok(())
}

fn toml_to_json(cfg: &str) -> Result<Json> {
    let toml_value = cfg.parse::<Value>()?;
    let toml_string = serde_json::to_string(&toml_value)?;
    let json = serde_json::from_str(&format!(r#"{{ "cfg": {} }}"#, &toml_string))?;
    Ok(json)
}

// merge two Json structs
fn merge(a: &mut Json, b: Json) {
    if let Json::Object(a_map) = a {
        if let Json::Object(b_map) = b {
            for (k, v) in b_map {
                merge(a_map.entry(k).or_insert(Json::Null), v);
            }
            return;
        }
    }
    *a = b;
}

fn create_with_template(ui: &mut UI,
                        render_dir: &std::path::Path,
                        file_name: &std::path::Path,
                        template: &str,
                        quiet: bool)
                        -> Result<()> {
    let path = Path::new(&render_dir).join(&file_name);
    if !quiet {
        ui.status(Status::Creating, format!("file: {}", path.display()))?;
    }

    create_dir_all(render_dir)?;

    // Write file to disk
    File::create(path).and_then(|mut file| file.write(template.as_bytes()))?;
    Ok(())
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: automatically add the model() method with a () return type when it is not found?
 * FIXME: Doing model.text.push_str() will not cause a set_text() to be added.
 * TODO: think about conditions and loops (widget-list).
 */

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate quote;
extern crate syn;

mod adder;
mod gen;
mod parser;
mod walker;

use std::collections::{HashMap, HashSet};
use std::sync::Mutex;

use adder::{Adder, Property};
use gen::gen;
use parser::Widget::Gtk;
use parser::{Widget, parse};
use quote::{Tokens, ToTokens};
use syn::{Delimited, FunctionRetTy, Ident, ImplItem, Mac, MethodSig, TokenTree, parse_expr, parse_item};
use syn::FnArg::Captured;
use syn::fold::Folder;
use syn::ImplItemKind::{Const, Macro, Method, Type};
use syn::ItemKind::Impl;
use syn::Ty;
use syn::visit::Visitor;
use walker::ModelVariableVisitor;

type PropertyModelMap = HashMap<Ident, HashSet<Property>>;

struct Component {
    model_type: Ty,
    msg_type: Ty,
    view_type: Ident,
}

lazy_static! {
    static ref COMPONENTS: Mutex<HashMap<Ident, Component>> = Mutex::new(HashMap::new());
}

#[derive(Debug)]
struct State {
    container_method: Option<ImplItem>,
    container_type: Option<ImplItem>,
    model_type: Option<ImplItem>,
    msg_type: Option<Ty>,
    properties_model_map: Option<PropertyModelMap>,
    root_widget: Option<Ident>,
    root_widget_type: Option<Ident>,
    update_method: Option<ImplItem>,
    view_macro: Option<Mac>,
    widget_model_type: Option<Ty>,
    widgets: HashMap<Ident, Ident>, // Map widget ident to widget type.
}

impl State {
    fn new() -> Self {
        State {
            container_method: None,
            container_type: None,
            model_type: None,
            msg_type: None,
            properties_model_map: None,
            root_widget: None,
            root_widget_type: None,
            update_method: None,
            view_macro: None,
            widget_model_type: None,
            widgets: HashMap::new(),
        }
    }
}

pub fn gen_widget(input: Tokens) -> Tokens {
    let source = input.to_string();
    let mut ast = parse_item(&source).unwrap();
    if let Impl(unsafety, polarity, generics, path, typ, items) = ast.node {
        let name = get_name(&typ);
        let mut new_items = vec![];
        let mut state = State::new();
        for item in items {
            let i = item.clone();
            match item.node {
                Const(_, _) => panic!("Unexpected const item"),
                Macro(mac) => state.view_macro = Some(mac),
                Method(sig, _) => {
                    match item.ident.to_string().as_ref() {
                        "container" => state.container_method = Some(i),
                        "model" => {
                            state.widget_model_type = Some(get_return_type(sig));
                            new_items.push(i);
                        },
                        "subscriptions" => new_items.push(i),
                        "update" => {
                            state.msg_type = Some(get_second_param_type(sig));
                            state.update_method = Some(i)
                        },
                        "update_command" => new_items.push(i),
                        method_name => panic!("Unexpected method {}", method_name),
                    }
                },
                Type(_) => {
                    if item.ident == Ident::new("Container") {
                        state.container_type = Some(i);
                    }
                    else if item.ident == Ident::new("Model") {
                        state.model_type = Some(i);
                    }
                    else {
                        panic!("Unexpected type item {:?}", item.ident);
                    }
                },
            }
        }
        let (view, map, relm_widgets) = get_view(&name, &mut state);
        state.properties_model_map = Some(map);
        new_items.push(view);
        state.widgets.insert(state.root_widget.clone().expect("root widget"),
            state.root_widget_type.clone().expect("root widget type"));
        new_items.push(get_model_type(state.model_type, state.widget_model_type));
        new_items.push(get_container_type(state.container_type, state.root_widget_type));
        new_items.push(get_update(state.update_method.expect("update method"),
            state.properties_model_map.expect("properties model map")));
        new_items.push(get_container(state.container_method, state.root_widget));
        let item = Impl(unsafety, polarity, generics, path, typ, new_items);
        ast.node = item;
        let widget_struct = create_struct(&name, state.widgets, relm_widgets);
        quote! {
            #widget_struct
            #ast
        }
    }
    else {
        panic!("Expected impl");
    }
}

fn add_widgets(widget: &Widget, widgets: &mut HashMap<Ident, Ident>, map: &PropertyModelMap) {
    // Only add widgets that are needed by the update() function.
    let mut to_add = false;
    for values in map.values() {
        for value in values {
            if value.widget_name == widget.name() {
                to_add = true;
            }
        }
    }
    if to_add {
        widgets.insert(widget.name().clone(), widget.typ().clone());
    }
    if let Gtk(ref gtk_widget) = *widget {
        for child in &gtk_widget.children {
            add_widgets(child, widgets, map);
        }
    }
}

fn block_to_impl_item(tokens: Tokens) -> ImplItem {
    let implementation = quote! {
        impl Test {
            #tokens
        }
    };
    let implementation = parse_item(implementation.as_str()).unwrap();
    match implementation.node {
        Impl(_, _, _, _, _, items) => items[0].clone(),
        _ => unreachable!(),
    }
}

fn create_struct(name: &Ident, widgets: HashMap<Ident, Ident>, relm_widgets: HashMap<Ident, Ident>) -> Tokens {
    let widgets = widgets.iter().filter(|&(ident, _)| !relm_widgets.contains_key(ident));
    let (idents, types): (Vec<_>, Vec<_>) = widgets.unzip();
    let relm_idents = relm_widgets.keys();
    let relm_types = relm_widgets.values();
    quote! {
        #[allow(dead_code)]
        #[derive(Clone)]
        struct #name {
            #(#idents: #types,)*
            #(#relm_idents: #relm_types,)*
        }
    }
}

fn get_container(container_method: Option<ImplItem>, root_widget: Option<Ident>) -> ImplItem {
    container_method.unwrap_or_else(|| {
        let root_widget = root_widget.expect("root widget");
        block_to_impl_item(quote! {
            fn container(&self) -> &Self::Container {
                &self.#root_widget
            }
        })
    })
}

fn get_container_type(container_type: Option<ImplItem>, root_widget_type: Option<Ident>) -> ImplItem {
    container_type.unwrap_or_else(|| {
        let root_widget_type = root_widget_type.expect("root widget type");
        block_to_impl_item(quote! {
            type Container = #root_widget_type;
        })
    })
}

fn get_model_type(model_type: Option<ImplItem>, widget_model_type: Option<Ty>) -> ImplItem {
    model_type.unwrap_or_else(|| {
        let widget_model_type = widget_model_type.expect("widget model type");
        block_to_impl_item(quote! {
            type Model = #widget_model_type;
        })
    })
}

fn get_name(typ: &Ty) -> Ident {
    if let Ty::Path(_, ref path) = *typ {
        let mut tokens = Tokens::new();
        path.to_tokens(&mut tokens);
        Ident::new(tokens.to_string())
    }
    else {
        panic!("Expected Path")
    }
}

/*
 * The map maps model variable name to a vector of tuples (widget name, property name).
 */
fn get_properties_model_map(widget: &Widget, map: &mut PropertyModelMap) {
    if let Gtk(ref gtk_widget) = *widget {
        for (name, value) in &gtk_widget.properties {
            let string = value.parse::<String>().unwrap();
            let expr = parse_expr(&string).unwrap();
            let mut visitor = ModelVariableVisitor::new();
            visitor.visit_expr(&expr);
            let model_variables = visitor.idents;
            for var in model_variables {
                let set = map.entry(var).or_insert_with(HashSet::new);
                set.insert(Property {
                    expr: string.clone(),
                    name: name.clone(),
                    widget_name: gtk_widget.name.clone(),
                });
            }
        }
        for child in &gtk_widget.children {
            get_properties_model_map(child, map);
        }
    }
}

fn get_return_type(sig: MethodSig) -> Ty {
    if let FunctionRetTy::Ty(ty) = sig.decl.output {
        ty
    }
    else {
        panic!("Unexpected default, expecting Ty");
    }
}

fn get_second_param_type(sig: MethodSig) -> Ty {
    if let Captured(_, ref path) = sig.decl.inputs[1] {
        path.clone()
    }
    else {
        panic!("Unexpected `{:?}`, expecting Captured Ty", sig.decl.inputs[1]);
    }
}

/*
 * TODO: Create a control flow graph for each variable of the model.
 * Add the set_property() calls in every leaf of every graphs.
 */
fn get_update(mut func: ImplItem, map: PropertyModelMap) -> ImplItem {
    if let Method(_, ref mut block) = func.node {
        let mut adder = Adder::new(&map);
        *block = adder.fold_block(block.clone());
    }
    // TODO: consider gtk::main_quit() as return.
    func
}

fn get_view(name: &Ident, state: &mut State) -> (ImplItem, PropertyModelMap, HashMap<Ident, Ident>) {
    {
        let ref segments = state.view_macro.as_ref().unwrap().path.segments;
        if segments.len() != 1 || segments[0].ident.to_string() != "view" {
            panic!("Unexpected macro item")
        }
    }
    impl_view(name, state)
}

fn impl_view(name: &Ident, state: &mut State) -> (ImplItem, PropertyModelMap, HashMap<Ident, Ident>) {
    let tokens = &state.view_macro.as_ref().unwrap().tts;
    if let TokenTree::Delimited(Delimited { ref tts, .. }) = tokens[0] {
        let mut widget = parse(tts);
        if let Gtk(ref mut widget) = widget {
            widget.relm_name = Some(name.clone());
            let mut components = COMPONENTS.lock().unwrap();
            components.insert(name.clone(), Component {
                msg_type: state.msg_type.clone().unwrap(),
                model_type: state.widget_model_type.clone().unwrap(),
                view_type: widget.gtk_type.clone(),
            });
        }
        let mut properties_model_map = HashMap::new();
        get_properties_model_map(&widget, &mut properties_model_map);
        add_widgets(&widget, &mut state.widgets, &properties_model_map);
        let idents = state.widgets.keys().collect();
        let (view, relm_widgets) = gen(name, widget, &mut state.root_widget, &mut state.root_widget_type, idents);
        let event_type = &state.msg_type;
        let item = block_to_impl_item(quote! {
            #[allow(unused_variables)] // Necessary to avoid warnings in case the parameters are unused.
            fn view(relm: ::relm::RemoteRelm<#event_type>, model: &Self::Model) -> Self {
                #view
            }
        });
        (item, properties_model_map, relm_widgets)
    }
    else {
        panic!("Expected `{{` but found `{:?}` in view! macro", tokens[0]);
    }
}
<commit_msg>Fix relm-gen-widget to export the generated struct<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: automatically add the model() method with a () return type when it is not found?
 * FIXME: Doing model.text.push_str() will not cause a set_text() to be added.
 * TODO: think about conditions and loops (widget-list).
 */

#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate quote;
extern crate syn;

mod adder;
mod gen;
mod parser;
mod walker;

use std::collections::{HashMap, HashSet};
use std::sync::Mutex;

use adder::{Adder, Property};
use gen::gen;
use parser::Widget::Gtk;
use parser::{Widget, parse};
use quote::{Tokens, ToTokens};
use syn::{Delimited, FunctionRetTy, Ident, ImplItem, Mac, MethodSig, TokenTree, parse_expr, parse_item};
use syn::FnArg::Captured;
use syn::fold::Folder;
use syn::ImplItemKind::{Const, Macro, Method, Type};
use syn::ItemKind::Impl;
use syn::Ty;
use syn::visit::Visitor;
use walker::ModelVariableVisitor;

type PropertyModelMap = HashMap<Ident, HashSet<Property>>;

struct Component {
    model_type: Ty,
    msg_type: Ty,
    view_type: Ident,
}

lazy_static! {
    static ref COMPONENTS: Mutex<HashMap<Ident, Component>> = Mutex::new(HashMap::new());
}

#[derive(Debug)]
struct State {
    container_method: Option<ImplItem>,
    container_type: Option<ImplItem>,
    model_type: Option<ImplItem>,
    msg_type: Option<Ty>,
    properties_model_map: Option<PropertyModelMap>,
    root_widget: Option<Ident>,
    root_widget_type: Option<Ident>,
    update_method: Option<ImplItem>,
    view_macro: Option<Mac>,
    widget_model_type: Option<Ty>,
    widgets: HashMap<Ident, Ident>, // Map widget ident to widget type.
}

impl State {
    fn new() -> Self {
        State {
            container_method: None,
            container_type: None,
            model_type: None,
            msg_type: None,
            properties_model_map: None,
            root_widget: None,
            root_widget_type: None,
            update_method: None,
            view_macro: None,
            widget_model_type: None,
            widgets: HashMap::new(),
        }
    }
}

pub fn gen_widget(input: Tokens) -> Tokens {
    let source = input.to_string();
    let mut ast = parse_item(&source).unwrap();
    if let Impl(unsafety, polarity, generics, path, typ, items) = ast.node {
        let name = get_name(&typ);
        let mut new_items = vec![];
        let mut state = State::new();
        for item in items {
            let i = item.clone();
            match item.node {
                Const(_, _) => panic!("Unexpected const item"),
                Macro(mac) => state.view_macro = Some(mac),
                Method(sig, _) => {
                    match item.ident.to_string().as_ref() {
                        "container" => state.container_method = Some(i),
                        "model" => {
                            state.widget_model_type = Some(get_return_type(sig));
                            new_items.push(i);
                        },
                        "subscriptions" => new_items.push(i),
                        "update" => {
                            state.msg_type = Some(get_second_param_type(sig));
                            state.update_method = Some(i)
                        },
                        "update_command" => new_items.push(i),
                        method_name => panic!("Unexpected method {}", method_name),
                    }
                },
                Type(_) => {
                    if item.ident == Ident::new("Container") {
                        state.container_type = Some(i);
                    }
                    else if item.ident == Ident::new("Model") {
                        state.model_type = Some(i);
                    }
                    else {
                        panic!("Unexpected type item {:?}", item.ident);
                    }
                },
            }
        }
        let (view, map, relm_widgets) = get_view(&name, &mut state);
        state.properties_model_map = Some(map);
        new_items.push(view);
        state.widgets.insert(state.root_widget.clone().expect("root widget"),
            state.root_widget_type.clone().expect("root widget type"));
        new_items.push(get_model_type(state.model_type, state.widget_model_type));
        new_items.push(get_container_type(state.container_type, state.root_widget_type));
        new_items.push(get_update(state.update_method.expect("update method"),
            state.properties_model_map.expect("properties model map")));
        new_items.push(get_container(state.container_method, state.root_widget));
        let item = Impl(unsafety, polarity, generics, path, typ, new_items);
        ast.node = item;
        let widget_struct = create_struct(&name, state.widgets, relm_widgets);
        quote! {
            #widget_struct
            #ast
        }
    }
    else {
        panic!("Expected impl");
    }
}

fn add_widgets(widget: &Widget, widgets: &mut HashMap<Ident, Ident>, map: &PropertyModelMap) {
    // Only add widgets that are needed by the update() function.
    let mut to_add = false;
    for values in map.values() {
        for value in values {
            if value.widget_name == widget.name() {
                to_add = true;
            }
        }
    }
    if to_add {
        widgets.insert(widget.name().clone(), widget.typ().clone());
    }
    if let Gtk(ref gtk_widget) = *widget {
        for child in &gtk_widget.children {
            add_widgets(child, widgets, map);
        }
    }
}

fn block_to_impl_item(tokens: Tokens) -> ImplItem {
    let implementation = quote! {
        impl Test {
            #tokens
        }
    };
    let implementation = parse_item(implementation.as_str()).unwrap();
    match implementation.node {
        Impl(_, _, _, _, _, items) => items[0].clone(),
        _ => unreachable!(),
    }
}

fn create_struct(name: &Ident, widgets: HashMap<Ident, Ident>, relm_widgets: HashMap<Ident, Ident>) -> Tokens {
    let widgets = widgets.iter().filter(|&(ident, _)| !relm_widgets.contains_key(ident));
    let (idents, types): (Vec<_>, Vec<_>) = widgets.unzip();
    let relm_idents = relm_widgets.keys();
    let relm_types = relm_widgets.values();
    quote! {
        #[allow(dead_code)]
        #[derive(Clone)]
        pub struct #name {
            #(#idents: #types,)*
            #(#relm_idents: #relm_types,)*
        }
    }
}

fn get_container(container_method: Option<ImplItem>, root_widget: Option<Ident>) -> ImplItem {
    container_method.unwrap_or_else(|| {
        let root_widget = root_widget.expect("root widget");
        block_to_impl_item(quote! {
            fn container(&self) -> &Self::Container {
                &self.#root_widget
            }
        })
    })
}

fn get_container_type(container_type: Option<ImplItem>, root_widget_type: Option<Ident>) -> ImplItem {
    container_type.unwrap_or_else(|| {
        let root_widget_type = root_widget_type.expect("root widget type");
        block_to_impl_item(quote! {
            type Container = #root_widget_type;
        })
    })
}

fn get_model_type(model_type: Option<ImplItem>, widget_model_type: Option<Ty>) -> ImplItem {
    model_type.unwrap_or_else(|| {
        let widget_model_type = widget_model_type.expect("widget model type");
        block_to_impl_item(quote! {
            type Model = #widget_model_type;
        })
    })
}

fn get_name(typ: &Ty) -> Ident {
    if let Ty::Path(_, ref path) = *typ {
        let mut tokens = Tokens::new();
        path.to_tokens(&mut tokens);
        Ident::new(tokens.to_string())
    }
    else {
        panic!("Expected Path")
    }
}

/*
 * The map maps model variable name to a vector of tuples (widget name, property name).
 */
fn get_properties_model_map(widget: &Widget, map: &mut PropertyModelMap) {
    if let Gtk(ref gtk_widget) = *widget {
        for (name, value) in &gtk_widget.properties {
            let string = value.parse::<String>().unwrap();
            let expr = parse_expr(&string).unwrap();
            let mut visitor = ModelVariableVisitor::new();
            visitor.visit_expr(&expr);
            let model_variables = visitor.idents;
            for var in model_variables {
                let set = map.entry(var).or_insert_with(HashSet::new);
                set.insert(Property {
                    expr: string.clone(),
                    name: name.clone(),
                    widget_name: gtk_widget.name.clone(),
                });
            }
        }
        for child in &gtk_widget.children {
            get_properties_model_map(child, map);
        }
    }
}

fn get_return_type(sig: MethodSig) -> Ty {
    if let FunctionRetTy::Ty(ty) = sig.decl.output {
        ty
    }
    else {
        panic!("Unexpected default, expecting Ty");
    }
}

fn get_second_param_type(sig: MethodSig) -> Ty {
    if let Captured(_, ref path) = sig.decl.inputs[1] {
        path.clone()
    }
    else {
        panic!("Unexpected `{:?}`, expecting Captured Ty", sig.decl.inputs[1]);
    }
}

/*
 * TODO: Create a control flow graph for each variable of the model.
 * Add the set_property() calls in every leaf of every graphs.
 */
fn get_update(mut func: ImplItem, map: PropertyModelMap) -> ImplItem {
    if let Method(_, ref mut block) = func.node {
        let mut adder = Adder::new(&map);
        *block = adder.fold_block(block.clone());
    }
    // TODO: consider gtk::main_quit() as return.
    func
}

fn get_view(name: &Ident, state: &mut State) -> (ImplItem, PropertyModelMap, HashMap<Ident, Ident>) {
    {
        let ref segments = state.view_macro.as_ref().unwrap().path.segments;
        if segments.len() != 1 || segments[0].ident.to_string() != "view" {
            panic!("Unexpected macro item")
        }
    }
    impl_view(name, state)
}

fn impl_view(name: &Ident, state: &mut State) -> (ImplItem, PropertyModelMap, HashMap<Ident, Ident>) {
    let tokens = &state.view_macro.as_ref().unwrap().tts;
    if let TokenTree::Delimited(Delimited { ref tts, .. }) = tokens[0] {
        let mut widget = parse(tts);
        if let Gtk(ref mut widget) = widget {
            widget.relm_name = Some(name.clone());
            let mut components = COMPONENTS.lock().unwrap();
            components.insert(name.clone(), Component {
                msg_type: state.msg_type.clone().unwrap(),
                model_type: state.widget_model_type.clone().unwrap(),
                view_type: widget.gtk_type.clone(),
            });
        }
        let mut properties_model_map = HashMap::new();
        get_properties_model_map(&widget, &mut properties_model_map);
        add_widgets(&widget, &mut state.widgets, &properties_model_map);
        let idents = state.widgets.keys().collect();
        let (view, relm_widgets) = gen(name, widget, &mut state.root_widget, &mut state.root_widget_type, idents);
        let event_type = &state.msg_type;
        let item = block_to_impl_item(quote! {
            #[allow(unused_variables)] // Necessary to avoid warnings in case the parameters are unused.
            fn view(relm: ::relm::RemoteRelm<#event_type>, model: &Self::Model) -> Self {
                #view
            }
        });
        (item, properties_model_map, relm_widgets)
    }
    else {
        panic!("Expected `{{` but found `{:?}` in view! macro", tokens[0]);
    }
}
<|endoftext|>