<commit_before>use crate::{Error, NixPath, Result};
use crate::errno::Errno;
use crate::fcntl::{self, OFlag};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::ptr;
use std::ffi;
use crate::sys;
use cfg_if::cfg_if;

#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};

#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};

/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
///    * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
///      if the path represents a file or directory).
///    * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
///      The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
///      after the `Dir` is dropped.
///    * can be iterated through multiple times without closing and reopening the file
///      descriptor. Each iteration rewinds when finished.
///    * returns entries for `.` (current directory) and `..` (parent directory).
///    * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
///      does).
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Dir(
    ptr::NonNull<libc::DIR>
);

impl Dir {
    /// Opens the given path as with `fcntl::open`.
    pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag,
                                     mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::open(path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Opens the given path as with `fcntl::openat`.
    pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag,
                                       mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::openat(dirfd, path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Converts from a descriptor-based object, closing the descriptor on success or failure.
    #[inline]
    pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
        Dir::from_fd(fd.into_raw_fd())
    }

    /// Converts from a file descriptor, closing it on success or failure.
    pub fn from_fd(fd: RawFd) -> Result<Self> {
        let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| {
            let e = Error::last();
            unsafe { libc::close(fd) };
            e
        })?;
        Ok(Dir(d))
    }

    /// Returns an iterator of `Result<Entry>` which rewinds when finished.
    pub fn iter(&mut self) -> Iter {
        Iter(self)
    }
}

// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}

impl AsRawFd for Dir {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { libc::dirfd(self.0.as_ptr()) }
    }
}

impl Drop for Dir {
    fn drop(&mut self) {
        let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
        if !std::thread::panicking() && e == Err(Errno::EBADF) {
            panic!("Closing an invalid file descriptor!");
        };
    }
}

fn next(dir: &mut Dir) -> Option<Result<Entry>> {
    unsafe {
        // Note: POSIX specifies that portable applications should dynamically allocate a
        // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
        // for the NUL byte. It doesn't look like the std library does this; it just uses
        // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
        // Probably fine here too then.
        let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
        let mut result = ptr::null_mut();
        if let Err(e) = Errno::result(
            readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result))
        {
            return Some(Err(e));
        }
        if result.is_null() {
            return None;
        }
        assert_eq!(result, ent.as_mut_ptr());
        Some(Ok(Entry(ent.assume_init())))
    }
}

#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);

impl<'d> Iterator for Iter<'d> {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        next(self.0)
    }
}

impl<'d> Drop for Iter<'d> {
    fn drop(&mut self) {
        unsafe { libc::rewinddir((self.0).0.as_ptr()) }
    }
}

/// The return type of [Dir::into_iter]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct OwningIter(Dir);

impl Iterator for OwningIter {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        next(&mut self.0)
    }
}

impl IntoIterator for Dir {
    type Item = Result<Entry>;
    type IntoIter = OwningIter;

    /// Creates a owning iterator, that is, one that takes ownership of the
    /// `Dir`. The `Dir` cannot be used after calling this.  This can be useful
    /// when you have a function that both creates a `Dir` instance and returns
    /// an `Iterator`.
    ///
    /// Example:
    ///
    /// ```
    /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
    /// use std::{iter::Iterator, string::String};
    ///
    /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
    ///     let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
    ///     d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
    /// }
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        OwningIter(self)
    }
}

/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Entry(dirent);

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
    Fifo,
    CharacterDevice,
    Directory,
    BlockDevice,
    File,
    Symlink,
    Socket,
}

impl Entry {
    /// Returns the inode number (`d_ino`) of the underlying `dirent`.
    #[allow(clippy::useless_conversion)]    // Not useless on all OSes
    pub fn ino(&self) -> u64 {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                         target_os = "emscripten",
                         target_os = "fuchsia",
                         target_os = "haiku",
                         target_os = "illumos",
                         target_os = "ios",
                         target_os = "l4re",
                         target_os = "linux",
                         target_os = "macos",
                         target_os = "solaris"))] {
                self.0.d_ino as u64
            } else {
                u64::from(self.0.d_fileno)
            }
        }
    }

    /// Returns the bare file name of this directory entry without any other leading path component.
    pub fn file_name(&self) -> &ffi::CStr {
        unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
    }

    /// Returns the type of this directory entry, if known.
    ///
    /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
    /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
    /// `fstat` if this returns `None`.
    pub fn file_type(&self) -> Option<Type> {
        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
        match self.0.d_type {
            libc::DT_FIFO => Some(Type::Fifo),
            libc::DT_CHR => Some(Type::CharacterDevice),
            libc::DT_DIR => Some(Type::Directory),
            libc::DT_BLK => Some(Type::BlockDevice),
            libc::DT_REG => Some(Type::File),
            libc::DT_LNK => Some(Type::Symlink),
            libc::DT_SOCK => Some(Type::Socket),
            /* libc::DT_UNKNOWN | */ _ => None,
        }

        // illumos and Solaris systems do not have the d_type member at all:
        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
        None
    }
}
<commit_msg>Impl `AsRawFd` for `OwningIter`<commit_after>use crate::{Error, NixPath, Result};
use crate::errno::Errno;
use crate::fcntl::{self, OFlag};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::ptr;
use std::ffi;
use crate::sys;
use cfg_if::cfg_if;

#[cfg(target_os = "linux")]
use libc::{dirent64 as dirent, readdir64_r as readdir_r};

#[cfg(not(target_os = "linux"))]
use libc::{dirent, readdir_r};

/// An open directory.
///
/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
///    * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
///      if the path represents a file or directory).
///    * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
///      The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
///      after the `Dir` is dropped.
///    * can be iterated through multiple times without closing and reopening the file
///      descriptor. Each iteration rewinds when finished.
///    * returns entries for `.` (current directory) and `..` (parent directory).
///    * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
///      does).
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Dir(
    ptr::NonNull<libc::DIR>
);

impl Dir {
    /// Opens the given path as with `fcntl::open`.
    pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag,
                                     mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::open(path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Opens the given path as with `fcntl::openat`.
    pub fn openat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P, oflag: OFlag,
                                       mode: sys::stat::Mode) -> Result<Self> {
        let fd = fcntl::openat(dirfd, path, oflag, mode)?;
        Dir::from_fd(fd)
    }

    /// Converts from a descriptor-based object, closing the descriptor on success or failure.
    #[inline]
    pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
        Dir::from_fd(fd.into_raw_fd())
    }

    /// Converts from a file descriptor, closing it on success or failure.
    pub fn from_fd(fd: RawFd) -> Result<Self> {
        let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(|| {
            let e = Error::last();
            unsafe { libc::close(fd) };
            e
        })?;
        Ok(Dir(d))
    }

    /// Returns an iterator of `Result<Entry>` which rewinds when finished.
    pub fn iter(&mut self) -> Iter {
        Iter(self)
    }
}

// `Dir` is not `Sync`. With the current implementation, it could be, but according to
// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
// call `readdir` simultaneously from multiple threads.
//
// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
unsafe impl Send for Dir {}

impl AsRawFd for Dir {
    fn as_raw_fd(&self) -> RawFd {
        unsafe { libc::dirfd(self.0.as_ptr()) }
    }
}

impl Drop for Dir {
    fn drop(&mut self) {
        let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
        if !std::thread::panicking() && e == Err(Errno::EBADF) {
            panic!("Closing an invalid file descriptor!");
        };
    }
}

fn next(dir: &mut Dir) -> Option<Result<Entry>> {
    unsafe {
        // Note: POSIX specifies that portable applications should dynamically allocate a
        // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
        // for the NUL byte. It doesn't look like the std library does this; it just uses
        // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
        // Probably fine here too then.
        let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
        let mut result = ptr::null_mut();
        if let Err(e) = Errno::result(
            readdir_r(dir.0.as_ptr(), ent.as_mut_ptr(), &mut result))
        {
            return Some(Err(e));
        }
        if result.is_null() {
            return None;
        }
        assert_eq!(result, ent.as_mut_ptr());
        Some(Ok(Entry(ent.assume_init())))
    }
}

#[derive(Debug, Eq, Hash, PartialEq)]
pub struct Iter<'d>(&'d mut Dir);

impl<'d> Iterator for Iter<'d> {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        next(self.0)
    }
}

impl<'d> Drop for Iter<'d> {
    fn drop(&mut self) {
        unsafe { libc::rewinddir((self.0).0.as_ptr()) }
    }
}

/// The return type of [Dir::into_iter]
#[derive(Debug, Eq, Hash, PartialEq)]
pub struct OwningIter(Dir);

impl Iterator for OwningIter {
    type Item = Result<Entry>;

    fn next(&mut self) -> Option<Self::Item> {
        next(&mut self.0)
    }
}

/// The file descriptor continues to be owned by the `OwningIter`,
/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
impl AsRawFd for OwningIter {
    fn as_raw_fd(&self) -> RawFd {
        self.0.as_raw_fd()
    }
}

impl IntoIterator for Dir {
    type Item = Result<Entry>;
    type IntoIter = OwningIter;

    /// Creates a owning iterator, that is, one that takes ownership of the
    /// `Dir`. The `Dir` cannot be used after calling this.  This can be useful
    /// when you have a function that both creates a `Dir` instance and returns
    /// an `Iterator`.
    ///
    /// Example:
    ///
    /// ```
    /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
    /// use std::{iter::Iterator, string::String};
    ///
    /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
    ///     let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
    ///     d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
    /// }
    /// ```
    fn into_iter(self) -> Self::IntoIter {
        OwningIter(self)
    }
}

/// A directory entry, similar to `std::fs::DirEntry`.
///
/// Note that unlike the std version, this may represent the `.` or `..` entries.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Entry(dirent);

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub enum Type {
    Fifo,
    CharacterDevice,
    Directory,
    BlockDevice,
    File,
    Symlink,
    Socket,
}

impl Entry {
    /// Returns the inode number (`d_ino`) of the underlying `dirent`.
    #[allow(clippy::useless_conversion)]    // Not useless on all OSes
    pub fn ino(&self) -> u64 {
        cfg_if! {
            if #[cfg(any(target_os = "android",
                         target_os = "emscripten",
                         target_os = "fuchsia",
                         target_os = "haiku",
                         target_os = "illumos",
                         target_os = "ios",
                         target_os = "l4re",
                         target_os = "linux",
                         target_os = "macos",
                         target_os = "solaris"))] {
                self.0.d_ino as u64
            } else {
                u64::from(self.0.d_fileno)
            }
        }
    }

    /// Returns the bare file name of this directory entry without any other leading path component.
    pub fn file_name(&self) -> &ffi::CStr {
        unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
    }

    /// Returns the type of this directory entry, if known.
    ///
    /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
    /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
    /// `fstat` if this returns `None`.
    pub fn file_type(&self) -> Option<Type> {
        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
        match self.0.d_type {
            libc::DT_FIFO => Some(Type::Fifo),
            libc::DT_CHR => Some(Type::CharacterDevice),
            libc::DT_DIR => Some(Type::Directory),
            libc::DT_BLK => Some(Type::BlockDevice),
            libc::DT_REG => Some(Type::File),
            libc::DT_LNK => Some(Type::Symlink),
            libc::DT_SOCK => Some(Type::Socket),
            /* libc::DT_UNKNOWN | */ _ => None,
        }

        // illumos and Solaris systems do not have the d_type member at all:
        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
        None
    }
}
<|endoftext|>


<commit_before>use ast::*;
use std::rc::Rc;

#[derive(Clone, PartialEq)]
pub struct Env {
    pub vars: Vec<ExprAst>,
    pub vals: Vec<ExprAst>,
    pub parent: Option<Rc<Env>>
}

#[allow(unreachable_code)]
#[allow(dead_code)]
impl Env {
    pub fn new() -> Env {
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            parent: None
        };
        res.setup();
        res
    }

    pub fn def_var(&mut self, var: ExprAst, val: ExprAst) {
        self.add_bingding(var, val);
    }

    pub fn add_bingding(&mut self, var: ExprAst, val: ExprAst) {
        assert!(self.vars.len() == self.vals.len());
        self.vars.push(var);
        self.vals.push(val);
    }

    pub fn lookup(&self, var: ExprAst) -> Option<ExprAst> {
        for i in range(0us, self.vars.len()).rev() {
            if self.vars[i] == var {
                return Some(self.vals[i].clone());
            }
        }
        match self.parent {
            Some(ref sub) => return sub.lookup(var),
            _ => {
                var.print();
                panic!("Not found:");
                return None;
            }
        };
    }

    pub fn extend(&self, vars: ExprAst, vals: ExprAst) -> Env {
        let mut _vars = vars;
        let mut _vals = vals;
        // FIXME: remove clone
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            parent: Some(Rc::new(self.clone()))
        };
        loop {
            if _vars.is_last() { break; }
            res.add_bingding(_vars.car(), _vals.car());
            _vars = _vars.cdr();
            _vals = _vals.cdr();
        }
        res.add_bingding(_vars.car(), _vals.car());
        return res;
    }

    fn setup(&mut self) {
        macro_rules! def_proc {
            ($func_name:ident, $raw_func_name:ident) => (
                fn $func_name(args: ExprAst) -> ExprAst {
                    ExprAst::Bool(BoolNode::new(args.car().$raw_func_name()))
                }
                )
        }

        macro_rules! add_proc {
            ($type_str:expr, $func_name:ident) => (
                self.def_var(ExprAst::Symbol(SymbolNode::new($type_str)),
                             ExprAst::Proc(ProcNode::new($func_name)))
                    )
        }

        def_proc!(is_null, is_empty);
        def_proc!(is_boolean, is_bool);
        def_proc!(is_symbol, is_symbol);
        def_proc!(is_string, is_string);
        def_proc!(is_pair, is_pair);
        def_proc!(is_char, is_char);
        def_proc!(is_int, is_int);

        add_proc!("null?", is_null);
        add_proc!("boolean?", is_boolean);
        add_proc!("symbol?", is_symbol);
        add_proc!("string?", is_string);
        add_proc!("char?", is_char);
        add_proc!("integer?", is_int);
        add_proc!("+", add);
        add_proc!("-", sub);
        add_proc!("*", mul);
        add_proc!("/", div);
        add_proc!("eq?", eq);
        add_proc!("=", eq);
        add_proc!("<", less);
        add_proc!(">", large);
        add_proc!("pair?", is_pair);
        add_proc!("car", car);
        add_proc!("cdr", cdr);
        add_proc!("cons", cons);
    }
}

fn add(args: ExprAst) -> ExprAst {
    let mut res = 0is;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res += exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn sub(args: ExprAst) -> ExprAst {
    let mut res = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        res -= exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn mul(args: ExprAst) -> ExprAst {
    let mut res = 1is;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res *= exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn div(args: ExprAst) -> ExprAst {
    let mut exps = args;
    let mut res = exps.car().as_int();
    exps = exps.cdr();
    loop {
        if exps.is_empty() { break; }
        let nxt = exps.car().as_int();
        if nxt == 0 {
            return ExprAst::Symbol(SymbolNode::new("Fail"));
        }
        res /= nxt;
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn cons(args: ExprAst) -> ExprAst {
    let obj1 = args.car();
    let obj2 = args.c("da");
    ExprAst::Pair(PairNode::new(obj1,
                                ExprAst::Pair(PairNode::new(obj2, ExprAst::Nil))))
}

fn eq(args: ExprAst) -> ExprAst {
    let obj1 = args.car();
    let obj2 = args.c("da");
    ExprAst::Bool(BoolNode::new(obj1 == obj2))
}

fn less(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val >= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    ExprAst::Bool(BoolNode::new(true))
}

fn car(args: ExprAst) -> ExprAst {
    assert!(args.car().is_pair());
    args.c("aa")
}

fn cdr(args: ExprAst) -> ExprAst {
    assert!(args.car().is_pair());
    args.c("ad")
}

fn large(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val <= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    ExprAst::Bool(BoolNode::new(true))
}

#[test]
fn test_env() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let val = env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    env.def_var(ExprAst::Str(StrNode::new("1")), ExprAst::Int(IntNode::new(1)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 1);

    env.def_var(ExprAst::Str(StrNode::new("1")), ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 2);

    env.def_var(ExprAst::Symbol(SymbolNode::new("sym")), ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("sym")));
    assert!(val.unwrap().as_int() == 2);

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("null?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("char?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("integer?")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new(">")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("<")));
    assert!(val.unwrap().is_proc());

}

#[test]
fn test_env_extend() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let vars = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("var")),
                                            ExprAst::Nil));

    let vals = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("val")),
                                            ExprAst::Nil));

    let mut extend_env = env.extend(vars, vals);
    let val = extend_env.lookup(ExprAst::Str(StrNode::new("var")));
    assert!(val.unwrap().as_str() == "val");

    let val = extend_env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    let vars = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("var_x")),
                                            ExprAst::Nil));

    let vals = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("val_x")),
                                            ExprAst::Nil));

    let mut extend_env = extend_env.extend(vars, vals);
    let val = extend_env.lookup(ExprAst::Str(StrNode::new("var_x")));
    assert!(val.unwrap().as_str() == "val_x");

    extend_env.def_var(ExprAst::Str(StrNode::new("1")),
                       ExprAst::Str(StrNode::new("1")));

    let val = extend_env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_str() == "1");
}
<commit_msg>remove clone from Env expand<commit_after>use ast::*;
use std::rc::Rc;

#[derive(Clone, PartialEq)]
pub struct Env {
    pub vars: Vec<ExprAst>,
    pub vals: Vec<ExprAst>,
    pub parent: Option<*const Env>
}

#[allow(unreachable_code)]
#[allow(dead_code)]
impl Env {
    pub fn new() -> Env {
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            parent: None
        };
        res.setup();
        res
    }

    pub fn def_var(&mut self, var: ExprAst, val: ExprAst) {
        self.add_bingding(var, val);
    }

    pub fn add_bingding(&mut self, var: ExprAst, val: ExprAst) {
        assert!(self.vars.len() == self.vals.len());
        self.vars.push(var);
        self.vals.push(val);
    }

    pub fn lookup(&self, var: ExprAst) -> Option<ExprAst> {
        for i in range(0us, self.vars.len()).rev() {
            if self.vars[i] == var {
                return Some(self.vals[i].clone());
            }
        }
        match self.parent {
            Some(sub) =>
                unsafe {
                    return (*sub).lookup(var)
                },
            _ => {
                var.print();
                panic!("Not found:");
                return None;
            }
        };
    }

    pub fn extend(&self, vars: ExprAst, vals: ExprAst) -> Env {
        let mut _vars = vars;
        let mut _vals = vals;
        // FIXME: remove clone
        let mut res = Env {
            vars: vec![],
            vals: vec![],
            parent: Some(self as (*const Env))
        };
        loop {
            if _vars.is_last() { break; }
            res.add_bingding(_vars.car(), _vals.car());
            _vars = _vars.cdr();
            _vals = _vals.cdr();
        }
        res.add_bingding(_vars.car(), _vals.car());
        return res;
    }

    fn setup(&mut self) {
        macro_rules! def_proc {
            ($func_name:ident, $raw_func_name:ident) => (
                fn $func_name(args: ExprAst) -> ExprAst {
                    ExprAst::Bool(BoolNode::new(args.car().$raw_func_name()))
                }
                )
        }

        macro_rules! add_proc {
            ($type_str:expr, $func_name:ident) => (
                self.def_var(ExprAst::Symbol(SymbolNode::new($type_str)),
                             ExprAst::Proc(ProcNode::new($func_name)))
                    )
        }

        def_proc!(is_null, is_empty);
        def_proc!(is_boolean, is_bool);
        def_proc!(is_symbol, is_symbol);
        def_proc!(is_string, is_string);
        def_proc!(is_pair, is_pair);
        def_proc!(is_char, is_char);
        def_proc!(is_int, is_int);

        add_proc!("null?", is_null);
        add_proc!("boolean?", is_boolean);
        add_proc!("symbol?", is_symbol);
        add_proc!("string?", is_string);
        add_proc!("char?", is_char);
        add_proc!("integer?", is_int);
        add_proc!("+", add);
        add_proc!("-", sub);
        add_proc!("*", mul);
        add_proc!("/", div);
        add_proc!("eq?", eq);
        add_proc!("=", eq);
        add_proc!("<", less);
        add_proc!(">", large);
        add_proc!("pair?", is_pair);
        add_proc!("car", car);
        add_proc!("cdr", cdr);
        add_proc!("cons", cons);
    }
}

fn add(args: ExprAst) -> ExprAst {
    let mut res = 0is;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res += exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn sub(args: ExprAst) -> ExprAst {
    let mut res = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        res -= exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn mul(args: ExprAst) -> ExprAst {
    let mut res = 1is;
    let mut exps = args;
    loop {
        if exps.is_empty() { break; }
        res *= exps.car().as_int();
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn div(args: ExprAst) -> ExprAst {
    let mut exps = args;
    let mut res = exps.car().as_int();
    exps = exps.cdr();
    loop {
        if exps.is_empty() { break; }
        let nxt = exps.car().as_int();
        if nxt == 0 {
            return ExprAst::Symbol(SymbolNode::new("Fail"));
        }
        res /= nxt;
        exps = exps.cdr();
    }
    ExprAst::Int(IntNode::new(res))
}

fn cons(args: ExprAst) -> ExprAst {
    let obj1 = args.car();
    let obj2 = args.c("da");
    ExprAst::Pair(PairNode::new(obj1,
                                ExprAst::Pair(PairNode::new(obj2, ExprAst::Nil))))
}

fn eq(args: ExprAst) -> ExprAst {
    let obj1 = args.car();
    let obj2 = args.c("da");
    ExprAst::Bool(BoolNode::new(obj1 == obj2))
}

fn less(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val >= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    ExprAst::Bool(BoolNode::new(true))
}

fn car(args: ExprAst) -> ExprAst {
    assert!(args.car().is_pair());
    args.c("aa")
}

fn cdr(args: ExprAst) -> ExprAst {
    assert!(args.car().is_pair());
    args.c("ad")
}

fn large(args: ExprAst) -> ExprAst {
    let val = args.car().as_int();
    let mut exps = args.cdr();
    loop {
        if exps.is_empty() { break; }
        if val <= exps.car().as_int() {
            return ExprAst::Bool(BoolNode::new(false));
        }
        exps = exps.cdr();
    }
    ExprAst::Bool(BoolNode::new(true))
}

#[test]
fn test_env() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let val = env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    env.def_var(ExprAst::Str(StrNode::new("1")), ExprAst::Int(IntNode::new(1)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 1);

    env.def_var(ExprAst::Str(StrNode::new("1")), ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_int() == 2);

    env.def_var(ExprAst::Symbol(SymbolNode::new("sym")), ExprAst::Int(IntNode::new(2)));
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("sym")));
    assert!(val.unwrap().as_int() == 2);

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("null?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("char?")));
    assert!(val.unwrap().is_proc());
    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("integer?")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new(">")));
    assert!(val.unwrap().is_proc());

    let val = env.lookup(ExprAst::Symbol(SymbolNode::new("<")));
    assert!(val.unwrap().is_proc());

}

#[test]
fn test_env_extend() {
    let mut env = Env::new();
    env.def_var(ExprAst::Str(StrNode::new("hello")),
                ExprAst::Str(StrNode::new("world")));

    let vars = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("var")),
                                            ExprAst::Nil));

    let vals = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("val")),
                                            ExprAst::Nil));

    let mut extend_env = env.extend(vars, vals);
    let val = extend_env.lookup(ExprAst::Str(StrNode::new("var")));
    assert!(val.unwrap().as_str() == "val");

    let val = extend_env.lookup(ExprAst::Str(StrNode::new("hello")));
    assert!(val.unwrap().as_str() == "world");

    let vars = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("var_x")),
                                            ExprAst::Nil));

    let vals = ExprAst::Pair(PairNode::new( ExprAst::Str(StrNode::new("val_x")),
                                            ExprAst::Nil));

    let mut extend_env = extend_env.extend(vars, vals);
    let val = extend_env.lookup(ExprAst::Str(StrNode::new("var_x")));
    assert!(val.unwrap().as_str() == "val_x");

    extend_env.def_var(ExprAst::Str(StrNode::new("1")),
                       ExprAst::Str(StrNode::new("1")));

    let val = extend_env.lookup(ExprAst::Str(StrNode::new("1")));
    assert!(val.unwrap().as_str() == "1");
}
<|endoftext|>


<commit_before>extern crate libc;
use std::str;
use std::ffi;
use std::ptr;
use self::libc::{size_t, c_char, time_t, uint32_t};

#[repr(C)]
struct memcached_st;

#[repr(C)]
#[derive(Debug)]
enum memcached_return_t {
    MEMCACHED_SUCCESS,
    MEMCACHED_FAILURE,
    MEMCACHED_HOST_LOOKUP_FAILURE, // getaddrinfo() and getnameinfo() only
    MEMCACHED_CONNECTION_FAILURE,
    MEMCACHED_CONNECTION_BIND_FAILURE,  // DEPRECATED, see MEMCACHED_HOST_LOOKUP_FAILURE
    MEMCACHED_WRITE_FAILURE,
    MEMCACHED_READ_FAILURE,
    MEMCACHED_UNKNOWN_READ_FAILURE,
    MEMCACHED_PROTOCOL_ERROR,
    MEMCACHED_CLIENT_ERROR,
    MEMCACHED_SERVER_ERROR, // Server returns "SERVER_ERROR"
    MEMCACHED_ERROR, // Server returns "ERROR"
    MEMCACHED_DATA_EXISTS,
    MEMCACHED_DATA_DOES_NOT_EXIST,
    MEMCACHED_NOTSTORED,
    MEMCACHED_STORED,
    MEMCACHED_NOTFOUND,
    MEMCACHED_MEMORY_ALLOCATION_FAILURE,
    MEMCACHED_PARTIAL_READ,
    MEMCACHED_SOME_ERRORS,
    MEMCACHED_NO_SERVERS,
    MEMCACHED_END,
    MEMCACHED_DELETED,
    MEMCACHED_VALUE,
    MEMCACHED_STAT,
    MEMCACHED_ITEM,
    MEMCACHED_ERRNO,
    MEMCACHED_FAIL_UNIX_SOCKET, // DEPRECATED
    MEMCACHED_NOT_SUPPORTED,
    MEMCACHED_NO_KEY_PROVIDED, /* Deprecated. Use MEMCACHED_BAD_KEY_PROVIDED! */
    MEMCACHED_FETCH_NOTFINISHED,
    MEMCACHED_TIMEOUT,
    MEMCACHED_BUFFERED,
    MEMCACHED_BAD_KEY_PROVIDED,
    MEMCACHED_INVALID_HOST_PROTOCOL,
    MEMCACHED_SERVER_MARKED_DEAD,
    MEMCACHED_UNKNOWN_STAT_KEY,
    MEMCACHED_E2BIG,
    MEMCACHED_INVALID_ARGUMENTS,
    MEMCACHED_KEY_TOO_BIG,
    MEMCACHED_AUTH_PROBLEM,
    MEMCACHED_AUTH_FAILURE,
    MEMCACHED_AUTH_CONTINUE,
    MEMCACHED_PARSE_ERROR,
    MEMCACHED_PARSE_USER_ERROR,
    MEMCACHED_DEPRECATED,
    MEMCACHED_IN_PROGRESS,
    MEMCACHED_SERVER_TEMPORARILY_DISABLED,
    MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE,
    MEMCACHED_MAXIMUM_RETURN, /* Always add new error code before */
    // MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE= MEMCACHED_ERROR
}

#[link(name = "memcached")]
extern {
    fn memcached(string: *const c_char, string_length: size_t) -> *const memcached_st;
    fn memcached_last_error_message(client: *const memcached_st) -> *const c_char;
    fn memcached_strerror(client: *const memcached_st, rc: memcached_return_t) -> *const c_char;
    fn memcached_set(client: *const memcached_st, key: *const c_char, key_length: size_t, value: *const c_char, value_length: size_t, expiration: time_t, flag: uint32_t) -> memcached_return_t;
    fn memcached_get(client: *const memcached_st, key: *const c_char, key_length: size_t, value_length: *mut size_t, flags: *mut uint32_t, error: *mut memcached_return_t) -> *const c_char;
    fn memcached_flush(client: *const memcached_st, expiration: time_t) -> memcached_return_t;

}

#[test]
fn test_memcaced() {
    unsafe {
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        println!("{:?}", client);
        assert!(!client.is_null());
    }
}

#[test]
fn test_memcached_last_error_message() {
    unsafe {
        let string = ffi::CString::new("foo").unwrap();
        let client = memcached(string.as_ptr(), 3);
        let error = memcached_last_error_message(client);
        let slice = ffi::CStr::from_ptr(error);
        println!("string returned: {}", str::from_utf8(slice.to_bytes()).unwrap());
        assert!(!error.is_null());
    }
}

#[test]
fn test_memcached_set() {
    unsafe{
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        assert!(!client.is_null());

        let key = ffi::CString::new("foo").unwrap().as_ptr();
        let key_length = 3;
        let value = ffi::CString::new("bar").unwrap().as_ptr();
        let value_length = 3;
        let r = memcached_set(client, key, key_length, value, value_length, 0, 0);

        match r {
            memcached_return_t::MEMCACHED_SUCCESS => {}
            _ => assert!(false)
        }
    }
}

#[test]
fn test_memcached_get() {
    unsafe{
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        assert!(!client.is_null());

        let key = ffi::CString::new("foo").unwrap().as_ptr();
        let key_length = 3;
        let value_length: *mut size_t = ptr::null_mut();
        let flags: *mut uint32_t = ptr::null_mut();
        let error: *mut memcached_return_t = ptr::null_mut();

        let r = memcached_get(client, key, key_length, value_length, flags, error);

        println!("xxxx {:?}", r);
        // match error {
        //     memcached_return_t::MEMCACHED_SUCCESS => {}
        //     _ => assert!(false)
        // }
    }
}

#[test]
fn test_memcached_flush() {
    unsafe{
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        assert!(!client.is_null());

        let r = memcached_flush(client, 0);
        match r {
            memcached_return_t::MEMCACHED_SUCCESS => {}
            _ => assert!(false)
        }
    }
}
<commit_msg>fix unit test<commit_after>extern crate libc;
use std::str;
use std::ffi;
use std::ptr;
use self::libc::{size_t, c_char, time_t, uint32_t};

#[repr(C)]
struct memcached_st;

#[repr(C)]
#[derive(Debug)]
enum memcached_return_t {
    MEMCACHED_SUCCESS,
    MEMCACHED_FAILURE,
    MEMCACHED_HOST_LOOKUP_FAILURE, // getaddrinfo() and getnameinfo() only
    MEMCACHED_CONNECTION_FAILURE,
    MEMCACHED_CONNECTION_BIND_FAILURE,  // DEPRECATED, see MEMCACHED_HOST_LOOKUP_FAILURE
    MEMCACHED_WRITE_FAILURE,
    MEMCACHED_READ_FAILURE,
    MEMCACHED_UNKNOWN_READ_FAILURE,
    MEMCACHED_PROTOCOL_ERROR,
    MEMCACHED_CLIENT_ERROR,
    MEMCACHED_SERVER_ERROR, // Server returns "SERVER_ERROR"
    MEMCACHED_ERROR, // Server returns "ERROR"
    MEMCACHED_DATA_EXISTS,
    MEMCACHED_DATA_DOES_NOT_EXIST,
    MEMCACHED_NOTSTORED,
    MEMCACHED_STORED,
    MEMCACHED_NOTFOUND,
    MEMCACHED_MEMORY_ALLOCATION_FAILURE,
    MEMCACHED_PARTIAL_READ,
    MEMCACHED_SOME_ERRORS,
    MEMCACHED_NO_SERVERS,
    MEMCACHED_END,
    MEMCACHED_DELETED,
    MEMCACHED_VALUE,
    MEMCACHED_STAT,
    MEMCACHED_ITEM,
    MEMCACHED_ERRNO,
    MEMCACHED_FAIL_UNIX_SOCKET, // DEPRECATED
    MEMCACHED_NOT_SUPPORTED,
    MEMCACHED_NO_KEY_PROVIDED, /* Deprecated. Use MEMCACHED_BAD_KEY_PROVIDED! */
    MEMCACHED_FETCH_NOTFINISHED,
    MEMCACHED_TIMEOUT,
    MEMCACHED_BUFFERED,
    MEMCACHED_BAD_KEY_PROVIDED,
    MEMCACHED_INVALID_HOST_PROTOCOL,
    MEMCACHED_SERVER_MARKED_DEAD,
    MEMCACHED_UNKNOWN_STAT_KEY,
    MEMCACHED_E2BIG,
    MEMCACHED_INVALID_ARGUMENTS,
    MEMCACHED_KEY_TOO_BIG,
    MEMCACHED_AUTH_PROBLEM,
    MEMCACHED_AUTH_FAILURE,
    MEMCACHED_AUTH_CONTINUE,
    MEMCACHED_PARSE_ERROR,
    MEMCACHED_PARSE_USER_ERROR,
    MEMCACHED_DEPRECATED,
    MEMCACHED_IN_PROGRESS,
    MEMCACHED_SERVER_TEMPORARILY_DISABLED,
    MEMCACHED_SERVER_MEMORY_ALLOCATION_FAILURE,
    MEMCACHED_MAXIMUM_RETURN, /* Always add new error code before */
    // MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE= MEMCACHED_ERROR
}

#[link(name = "memcached")]
extern {
    fn memcached(string: *const c_char, string_length: size_t) -> *const memcached_st;
    fn memcached_last_error_message(client: *const memcached_st) -> *const c_char;
    fn memcached_strerror(client: *const memcached_st, rc: memcached_return_t) -> *const c_char;
    fn memcached_set(client: *const memcached_st, key: *const c_char, key_length: size_t, value: *const c_char, value_length: size_t, expiration: time_t, flag: uint32_t) -> memcached_return_t;
    fn memcached_get(client: *const memcached_st, key: *const c_char, key_length: size_t, value_length: *mut size_t, flags: *mut uint32_t, error: *mut memcached_return_t) -> *const c_char;
    fn memcached_flush(client: *const memcached_st, expiration: time_t) -> memcached_return_t;

}

#[test]
fn test_memcaced() {
    unsafe {
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        println!("{:?}", client);
        assert!(!client.is_null());
    }
}

#[test]
fn test_memcached_last_error_message() {
    unsafe {
        let string = ffi::CString::new("foo").unwrap();
        let client = memcached(string.as_ptr(), 3);
        let error = memcached_last_error_message(client);
        let slice = ffi::CStr::from_ptr(error);
        println!("string returned: {}", str::from_utf8(slice.to_bytes()).unwrap());
        assert!(!error.is_null());
    }
}

#[test]
fn test_memcached_operations() {
    unsafe{
        // client
        let s = "--SERVER=localhost";
        let string = ffi::CString::new(s).unwrap();
        let client = memcached(string.as_ptr(), 18);
        assert!(!client.is_null());

        // flush
        let r = memcached_flush(client, 0);
        match r {
            memcached_return_t::MEMCACHED_SUCCESS => {}
            _ => assert!(false)
        }

        // set foo bar
        let key = ffi::CString::new("foo").unwrap();
        let key_length = 3;
        let value = ffi::CString::new("bar").unwrap();
        let value_length = 3;
        let r = memcached_set(client, key.as_ptr(), key_length, value.as_ptr(), value_length, 0, 0);

        match r {
            memcached_return_t::MEMCACHED_SUCCESS => {}
            _ => assert!(false)
        }

        // get foo == bar
        let mut value_length: size_t = 0;
        let value_length_ptr: *mut size_t = &mut value_length;

        let mut flags: uint32_t = 0;
        let flags_ptr: *mut uint32_t = &mut flags;

        let mut error: memcached_return_t = memcached_return_t::MEMCACHED_FAILURE;
        let error_ptr: *mut memcached_return_t = &mut error;

        let r = memcached_get(client, key.as_ptr(), key_length, value_length_ptr, flags_ptr, error_ptr);

        println!("value: {:?}, error: {:?}, value_length: {:?}", r, error_ptr, value_length_ptr);
        assert!(value_length == 3);
        match error {
            memcached_return_t::MEMCACHED_SUCCESS => {}
            _ => assert!(false)
        }
        let slice = ffi::CStr::from_ptr(r);
        assert!(str::from_utf8(slice.to_bytes()).unwrap() == "bar");
    }
}
<|endoftext|>


<commit_before>use std::slice;

extern crate libc;
use self::libc::{c_void, c_double, size_t};

use super::geo::{Point, Polygon, LineString};

use super::polylabel;
use super::num::Float;

/// Wrapper for a void pointer to a sequence of [`Array`](struct.Array.html)s, and the sequence length
///
/// Used for inner Polygon rings
#[repr(C)]
pub struct WrapperArray {
    pub data: *const Array,
    pub len: size_t,
}

/// Wrapper for a void pointer to a sequence of 2-element arrays representing points, and the sequence length
///
/// Used for outer Polygon rings. `data` is a `Vec<[c_double; 2]>`
#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: size_t,
}

/// FFI struct for returned optimum Polygon label position
#[repr(C)]
pub struct Position {
    pub x_pos: c_double,
    pub y_pos: c_double,
}

// convert a Polylabel result Point into values that can be sent across the FFI boundary
impl<T> From<Point<T>> for Position
    where T: Float
{
    fn from(point: Point<T>) -> Position {
        Position {
            x_pos: point.x().to_f64().unwrap() as c_double,
            y_pos: point.y().to_f64().unwrap() as c_double,
        }
    }
}

fn reconstitute(arr: &Array) -> Vec<[f64; 2]> {
    unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
}

fn reconstitute2(arr: WrapperArray) -> Vec<Vec<[f64; 2]>> {
    let arrays = unsafe { slice::from_raw_parts(arr.data as *mut Array, arr.len) };
    arrays.into_iter().map(|x| reconstitute(x)).collect()
}

/// FFI access to the [`polylabel`](fn.polylabel.html) function
///
/// Accepts three arguments: an exterior ring [`Array`](struct.Array.html), an interior rings [`WrapperArray`](struct.WrapperArray.html), and a tolerance `c_float`.
#[no_mangle]
pub extern "C" fn polylabel_ffi(outer: Array,
                                inners: WrapperArray,
                                tolerance: c_double)
                                -> Position {
    let exterior: Vec<[f64; 2]> =
        unsafe { slice::from_raw_parts(outer.data as *mut [c_double; 2], outer.len).to_vec() };
    let interior: Vec<Vec<[f64; 2]>> = reconstitute2(inners);
    let ls_ext = LineString(exterior.iter().map(|e| Point::new(e[0], e[1])).collect());
    let ls_int: Vec<LineString<c_double>> = interior.iter()
        .map(|vec| LineString(vec.iter().map(|e| Point::new(e[0], e[1])).collect()))
        .collect();

    let poly = Polygon(ls_ext, ls_int);
    polylabel(&poly, &tolerance).into()
}

#[cfg(test)]
mod tests {
    use super::{Array, WrapperArray, reconstitute2};
    use super::libc::{c_void, size_t};
    use std::mem;

    // Only used for testing
    fn gen_array(v: Vec<[f64; 2]>) -> Array {
        let array = Array {
            data: v.as_ptr() as *const c_void,
            len: v.len() as size_t,
        };
        mem::forget(v);
        array
    }
    // only used for testing
    fn gen_wrapperarray(v: Vec<Vec<[f64; 2]>>) -> WrapperArray {
        let converted: Vec<Array> = v.into_iter().map(|x| gen_array(x)).collect();
        let array2 = WrapperArray {
            data: converted.as_ptr() as *const Array,
            len: converted.len() as size_t,
        };
        mem::forget(converted);
        array2
    }
    #[test]
    fn test_array() {
        let i_a = vec![[0.5, 0.5], [1.0, 1.0], [1.5, 0.5]];
        let i_b = vec![[0.55, 0.55], [0.8, 0.8], [1.2, 0.55]];
        let inners = vec![i_a, i_b];
        let array = gen_wrapperarray(inners);
        let rec_inners = reconstitute2(array);
        assert_eq!(rec_inners[0][2], [1.5, 0.5])
    }

}
<commit_msg>Add FFI test<commit_after>use std::slice;

extern crate libc;
use self::libc::{c_void, c_double, size_t};

use super::geo::{Point, Polygon, LineString};

use super::polylabel;
use super::num::Float;

/// Wrapper for a void pointer to a sequence of [`Array`](struct.Array.html)s, and the sequence length
///
/// Used for inner Polygon rings
#[repr(C)]
pub struct WrapperArray {
    pub data: *const Array,
    pub len: size_t,
}

/// Wrapper for a void pointer to a sequence of 2-element arrays representing points, and the sequence length
///
/// Used for outer Polygon rings. `data` is a `Vec<[c_double; 2]>`
#[repr(C)]
pub struct Array {
    pub data: *const c_void,
    pub len: size_t,
}

/// FFI struct for returned optimum Polygon label position
#[repr(C)]
pub struct Position {
    pub x_pos: c_double,
    pub y_pos: c_double,
}

// convert a Polylabel result Point into values that can be sent across the FFI boundary
impl<T> From<Point<T>> for Position
    where T: Float
{
    fn from(point: Point<T>) -> Position {
        Position {
            x_pos: point.x().to_f64().unwrap() as c_double,
            y_pos: point.y().to_f64().unwrap() as c_double,
        }
    }
}

fn reconstitute(arr: &Array) -> Vec<[f64; 2]> {
    unsafe { slice::from_raw_parts(arr.data as *mut [f64; 2], arr.len).to_vec() }
}

fn reconstitute2(arr: WrapperArray) -> Vec<Vec<[f64; 2]>> {
    let arrays = unsafe { slice::from_raw_parts(arr.data as *mut Array, arr.len) };
    arrays.into_iter().map(|x| reconstitute(x)).collect()
}

/// FFI access to the [`polylabel`](fn.polylabel.html) function
///
/// Accepts three arguments: an exterior ring [`Array`](struct.Array.html), an interior rings [`WrapperArray`](struct.WrapperArray.html), and a tolerance `c_float`.
#[no_mangle]
pub extern "C" fn polylabel_ffi(outer: Array,
                                inners: WrapperArray,
                                tolerance: c_double)
                                -> Position {
    let exterior: Vec<[f64; 2]> =
        unsafe { slice::from_raw_parts(outer.data as *mut [c_double; 2], outer.len).to_vec() };
    let interior: Vec<Vec<[f64; 2]>> = reconstitute2(inners);
    let ls_ext = LineString(exterior.iter().map(|e| Point::new(e[0], e[1])).collect());
    let ls_int: Vec<LineString<c_double>> = interior.iter()
        .map(|vec| LineString(vec.iter().map(|e| Point::new(e[0], e[1])).collect()))
        .collect();
    let poly = Polygon(ls_ext, ls_int);
    polylabel(&poly, &tolerance).into()
}

#[cfg(test)]
mod tests {
    use super::{Array, WrapperArray, polylabel_ffi, reconstitute2};
    use super::libc::{c_void, size_t};
    use geo::Point;
    use std::mem;

    // Only used for testing
    fn gen_array(v: Vec<[f64; 2]>) -> Array {
        let array = Array {
            data: v.as_ptr() as *const c_void,
            len: v.len() as size_t,
        };
        mem::forget(v);
        array
    }
    // only used for testing
    fn gen_wrapperarray(v: Vec<Vec<[f64; 2]>>) -> WrapperArray {
        let converted: Vec<Array> = v.into_iter().map(|x| gen_array(x)).collect();
        let array2 = WrapperArray {
            data: converted.as_ptr() as *const Array,
            len: converted.len() as size_t,
        };
        mem::forget(converted);
        array2
    }
    #[test]
    fn test_array() {
        let i_a = vec![[0.5, 0.5], [1.0, 1.0], [1.5, 0.5]];
        let i_b = vec![[0.55, 0.55], [0.8, 0.8], [1.2, 0.55]];
        let inners = vec![i_a, i_b];
        let array = gen_wrapperarray(inners);
        let rec_inners = reconstitute2(array);
        assert_eq!(rec_inners[0][2], [1.5, 0.5])
    }
    #[test]
    fn test_ffi() {
        let ext_vec = vec![[4.0, 1.0], [5.0, 2.0], [5.0, 3.0], [4.0, 4.0], [3.0, 4.0], [2.0, 3.0],
                           [2.0, 2.0], [3.0, 1.0], [4.0, 1.0]];
        let int_vec = vec![vec![[3.5, 3.5], [4.4, 2.0], [2.6, 2.0], [3.5, 3.5]],
                           vec![[4.0, 3.0], [4.0, 3.2], [4.5, 3.2], [4.0, 3.0]]];

        let outer = gen_array(ext_vec);
        let inners = gen_wrapperarray(int_vec);
        let res = polylabel_ffi(outer, inners, 0.1);
        let res_point = Point::new(res.x_pos, res.y_pos);
        assert_eq!(res_point, Point::new(3.125, 2.875));
    }

}
<|endoftext|>


<commit_before>extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;

use lib::DownloadError;

use CONFIG;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub qid: i64,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

impl DownloadDB {
    pub fn update_video(&mut self,url: String, qid: i64){
        self.qid = qid;
        self.url = url;
    }
    pub fn update_folder(&mut self, folder: String){
        self.folder = folder;
    }
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download audio_raw codec for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);

        let curr_quality = if download_audio {
            &CONFIG.codecs.audio_raw
        }else{
            &self.ddb.quality
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d%");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.find(&text) {
                            Some(s) => { //println!("Match at {}", s.0);
                                        println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                    },
                            None => {},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    ///Get playlist file ids
    pub fn get_playlist(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_extract());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_playlist: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    pub fn get_playlist_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_get_name());
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let re = regex!(r"[download] Downloading playlist: (.*)");

        let mut name: Option<String> = None;
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        if name.is_none() {
                            match re.captures(&text) {
                                Some(cap) => {
                                            println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                            name = Some(cap.at(1).unwrap().to_string());
                                        },
                                None => {},
                            }
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        name.ok_or(DownloadError::DownloadError("no playlist name".to_string()))
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_extract(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--print-json")
                                    .arg("--flat-playlist")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_get_name(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--no-warnings")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"step (\d)");

        let mut last_line = String::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { //println!("Match at {}", s.0);
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &cap.at(1).unwrap().to_string()));
                                    },
                            None => {last_line = text.clone()},
                        }
                    },
            }
        }

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let out = last_line[last_line.find("filename ").unwrap()+9..].trim().to_string();
        //stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() && !self.is_aac() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        if self.ddb.quality == CONFIG.codecs.audio_raw {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_source_hq {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_mp3 {
            true
        } else {
            false
        }
    }

    ///Check if is AAC file
    fn is_aac(&self) -> bool {
        CONFIG.extensions.aac.contains(&self.ddb.quality)
    }
}<commit_msg>added process killing, preventing youtube-dl from extracting more details on playlist name retrieval<commit_after>extern crate regex;
use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

use std::process::{Command, Stdio, Child};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::Path;
use lib::config::ConfigGen;
use std::convert::Into;

use lib::DownloadError;

use CONFIG;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

#[derive(Clone)]
pub struct DownloadDB {
    pub url: String,
    pub quality: i16,
    pub playlist: bool,
    pub compress: bool,
    pub qid: i64,
    pub folder: String, // download folder, changes for playlists
    pub pool: MyPool,
}

impl DownloadDB {
    pub fn update_video(&mut self,url: String, qid: i64){
        self.qid = qid;
        self.url = url;
    }
    pub fn update_folder(&mut self, folder: String){
        self.folder = folder;
    }
}

pub struct Downloader<'a> {
    pub ddb: &'a DownloadDB,
    defaults: &'a ConfigGen,
    // pool: MyPool,
}

impl<'a> Downloader<'a>{
    pub fn new(ddb: &'a DownloadDB, defaults: &'a ConfigGen) -> Downloader<'a>{
        Downloader {ddb: ddb, defaults: defaults}
    }

    ///Regex matching: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
    ///
    ///Downloads a video, updates the DB
    ///TODO: get the sql statements out of the class
    ///TODO: wrap errors
    ///Doesn't care about DMCAs, will emit errors on them
    ///download_audio: ignore quality & download audio_raw codec for split containers
    pub fn download_file(&self, file_path: &str, download_audio: bool) -> Result<bool,DownloadError> {
        println!("{:?}", self.ddb.url);

        let curr_quality = if download_audio {
            &CONFIG.codecs.audio_raw
        }else{
            &self.ddb.quality
        };

        println!("quality: {}",curr_quality);
        let mut child = try!(self.run_download_process(file_path,curr_quality));
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"\d+\.\d%");

        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.find(&text) {
                            Some(s) => { //println!("Match at {}", s.0);
                                        println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &text[s.0..s.1].to_string()));
                                    },
                            None => {},
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        Ok(true)
    }

    ///Trys to get the original name of a file, while checking for availability
    ///
    pub fn get_file_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_filename_process());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_file_name: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    ///Get playlist file ids
    pub fn get_playlist(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_extract());
        let mut stdout_buffer = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());
        //println!("stderr: {:?}", stderr);
        //println!("stdout: {:?}", stdout);
        if stderr.is_empty() == true {
            println!("get_playlist: {:?}", stdout);
            Ok(stdout.trim().to_string())
        }else{
            if stderr.contains("not available in your country") || stderr.contains("contains content from") {
                return Err(DownloadError::DMCAError);
            }else{
                return Err(DownloadError::DownloadError(stderr));
            }
        }
    }

    ///Retrive playlist name, will kill the process due to yt-dl starting detail retrieval
    pub fn get_playlist_name(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.run_playlist_get_name());
        let stdout = BufReader::new(child.stdout.take().unwrap());

        let re = regex!(r"[download] Downloading playlist: (.*)");

        let mut name: Option<String> = None;
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        if name.is_none() {
                            match re.captures(&text) {
                                Some(cap) => {
                                            println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                            name = Some(cap.at(1).unwrap().to_string());
                                            try!(child.kill());
                                        },
                                None => {},
                            }
                        }
                    },
            }
        }

        try!(child.wait()); // waits for finish & then exists zombi process fixes #10

        name.ok_or(DownloadError::DownloadError("no playlist name".to_string()))
    }

    fn run_download_process(&self, file_path: &str, quality: &i16) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--newline")
                                    .arg("-r")
                                    .arg(format!("{}M",self.defaults.download_mbps))
                                    .arg("-f")
                                    .arg(quality.to_string())
                                    .arg("-o")
                                    .arg(file_path)
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_filename_process(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("--get-filename")
                                    .arg("-o")
                                    .arg("%(title)s")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .stderr(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_extract(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--print-json")
                                    .arg("--flat-playlist")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    fn run_playlist_get_name(&self) -> Result<Child,DownloadError> {
        match Command::new("youtube-dl")
                                    .arg("-s")
                                    .arg("--no-warnings")
                                    .arg(&self.ddb.url)
                                    .stdin(Stdio::null())
                                    .stdout(Stdio::piped())
                                    .spawn() {
            Err(why) => Err(DownloadError::InternalError(Error::description(&why).into())),
            Ok(process) => Ok(process),
        }
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: &String) -> Result<(),DownloadError>{
        try!(stmt.execute(&[progress,&self.ddb.qid]).map(|_| Ok(())))
        //-> only return errors, ignore the return value of stmt.execute
    }

    ///This function does a 3rd party binding in case it's needed
    ///due to the country restrictions
    ///Because hyper doesn't support timeout settings atm, we're calling an external
    ///lib
    ///The returned value contains the original video name, the lib downloads & saves
    ///the file at the given folder under the given name
    pub fn lib_request_video(&self) -> Result<String,DownloadError> {
        let mut child = try!(self.lib_request_video_cmd());
        println!("Requesting video via lib..");
        let stdout = BufReader::new(child.stdout.take().unwrap());
        let mut stderr_buffer = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.ddb.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"step (\d)");

        let mut last_line = String::new();
        for line in stdout.lines(){
            match line{
                Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
                Ok(text) => {
                        println!("Out: {}",text);
                        match re.captures(&text) {
                            Some(cap) => { //println!("Match at {}", s.0);
                                        println!("{}", cap.at(1).unwrap()); // ONLY with ASCII chars makeable!
                                        try!(self.update_progress(&mut statement, &cap.at(1).unwrap().to_string()));
                                    },
                            None => {last_line = text.clone()},
                        }
                    },
            }
        }

        let mut stderr: String = String::new();
        try!(stderr_buffer.read_to_string(&mut stderr));

        try!(child.wait());

        if !stderr.is_empty() {
            println!("stderr: {:?}", stderr);
            return Err(DownloadError::InternalError(stderr));
        }
        //this ONLY works because `filename ` is ascii..
        let out = last_line[last_line.find("filename ").unwrap()+9..].trim().to_string();
        //stdout.trim();

        Ok(out)
    }

    ///Generate the lib-cmd `request [..]?v=asdf -folder /downloads -a -name testfile`
    fn lib_request_video_cmd(&self) -> Result<Child,DownloadError> {
        let java_path = Path::new(&self.defaults.jar_cmd);
        println!("{:?}", format!("{}/java -jar {}/offliberty.jar",self.defaults.jar_cmd,self.defaults.jar_folder));
        match Command::new("./java")
                                        .current_dir(java_path)
                                        .arg("-jar")
                                        .arg(format!("{}/offliberty.jar",&self.defaults.jar_folder))
                                        .arg("request")
                                        .arg(&self.ddb.url)
                                        .arg("-folder")
                                        .arg(&self.ddb.folder)
                                        .arg(self.gen_request_str())
                                        .arg("-name")
                                        .arg(self.ddb.qid.to_string()) //eq. format! https://botbot.me/mozilla/rust/msg/37524131/
                                        .stdin(Stdio::null())
                                        .stdout(Stdio::piped())
                                        .stderr(Stdio::piped())
                                        .spawn() {
                Err(why) => {println!("{:?}",why); Err(DownloadError::InternalError(Error::description(&why).into()))},
                Ok(process) => Ok(process),
            }
    }

    ///Generate -a or -v, based on if an audio or video quality is requested
    fn gen_request_str(&self) -> &'a str{
        if self.is_audio() && !self.is_aac() {
            "-a"
        } else {
            "-v"
        }
    }

    ///Check if the quality is 141, standing for audio or not
    pub fn is_audio(&self) -> bool {
        if self.ddb.quality == CONFIG.codecs.audio_raw {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_source_hq {
            true
        } else if self.ddb.quality == CONFIG.codecs.audio_mp3 {
            true
        } else {
            false
        }
    }

    ///Check if is AAC file
    fn is_aac(&self) -> bool {
        CONFIG.extensions.aac.contains(&self.ddb.quality)
    }
}<|endoftext|>


<commit_before>extern crate git2;
extern crate regex;
use super::Result;

use git2::build::RepoBuilder;
use std::path::Path;
use regex::Regex;

#[derive(Debug, PartialEq)]
pub enum Url {
    Local(String),
    Github(String, String),
    Remote(String),
}

impl Url {
    /// supports two types git of repository urls
    /// local repositories that start with file://
    /// and github repositories ower/repo
    pub fn from_str(txt: &str) -> Option<Url> {
        lazy_static! {
            static ref LOCAL: Regex = Regex::new(r#"^file://(\S+)$""#).unwrap();
            static ref GH: Regex = Regex::new(r#"^([^\s/]+)/([^\s/]+?)$"#).unwrap();
            static ref REMOTE: Regex = Regex::new(
                r#"^(git[@|://].*)|(https://.*)|(http://.*)|(ssh://.*)$"#
            ).unwrap();
        }
        LOCAL.captures(txt)
            .map(|caps| Url::Local(caps.get(1).unwrap().as_str().to_owned()))
            .or_else(|| {
                if REMOTE.is_match(txt) {
                    Some(Url::Remote(txt.to_string()))
                } else {
                    None
                }
            })
            .or_else(|| {
                GH.captures(txt)
                    .map(|caps| {
                        Url::Github(caps.get(1).unwrap().as_str().to_owned(),
                                    caps.get(2).unwrap().as_str().to_owned())
                    })
            })
    }
}

/// clone a repository at a rev to a directory
/// a best attempt effort is made to authenticate
/// requests when required to support private
/// git repositories
pub fn clone<P, R>(repo: Url, dir: P, rev: R) -> Result<()>
    where P: AsRef<Path>,
          R: Into<String>
{
    let mut cb = git2::RemoteCallbacks::new();
    let mut tried_sshkey = false;
    cb.credentials(move |url, username, cred_type| {
        if cred_type.contains(git2::USER_PASS_PLAINTEXT) {
            let cfg = git2::Config::open_default().unwrap();
            return git2::Cred::credential_helper(&cfg, url, username);
        }
        if cred_type.contains(git2::SSH_KEY) && !tried_sshkey {

            // If ssh-agent authentication fails, libgit2 will keep
            // calling this callback asking for other authentication
            // methods to try. Make sure we only try ssh-agent once,
            // to avoid looping forever.
            tried_sshkey = true;
            let username = username.unwrap();
            return git2::Cred::ssh_key_from_agent(&username);
        }
        Err(git2::Error::from_str("no authentication available"))
    });

    let mut fo = git2::FetchOptions::new();
    fo.remote_callbacks(cb).download_tags(git2::AutotagOption::All);
    let url = match repo {
        Url::Github(ref owner, ref repo) => format!("git://github.com/{}/{}.git", owner, repo),
        Url::Local(ref path) => path.to_owned(),
        Url::Remote(ref remote) => remote.to_owned(),
    };
    RepoBuilder::new().branch(&rev.into())
        .fetch_options(fo)
        .clone(&url, dir.as_ref())?;

    debug!("cloned {:?} to {:?}", repo, dir.as_ref());
    Ok(())
}

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

    #[test]
    fn test_authenticated_ssh_url() {
        assert_eq!(Url::from_str("git@github.com:user/repo.git"), Some(Url::Remote(String::from("git@github.com:user/repo.git"))))
    }

    #[test]
    fn test_public_ssh_url() {
        assert_eq!(Url::from_str("git://github.com:user/repo.git"), Some(Url::Remote(String::from("git://github.com:user/repo.git"))))
    }

    #[test]
    fn test_https_url() {
        assert_eq!(Url::from_str("https://github.com/user/repo.git"), Some(Url::Remote(String::from("https://github.com/user/repo.git"))))
    }

    #[test]
    fn test_github_url() {
        assert_eq!(Url::from_str("user/repo"), Some(Url::Github(String::from("user"), String::from("repo"))))
    }
}
<commit_msg>fix broken regex for file URIs (#16)<commit_after>extern crate git2;
extern crate regex;
use super::Result;

use git2::build::RepoBuilder;
use std::path::Path;
use regex::Regex;

#[derive(Debug, PartialEq)]
pub enum Url {
    Local(String),
    Github(String, String),
    Remote(String),
}

impl Url {
    /// supports two types git of repository urls
    /// local repositories that start with file://
    /// and github repositories ower/repo
    pub fn from_str(txt: &str) -> Option<Url> {
        lazy_static! {
            static ref LOCAL: Regex = Regex::new(r#"^file://(\S+)$"#).unwrap();
            static ref GH: Regex = Regex::new(r#"^([^\s/]+)/([^\s/]+?)$"#).unwrap();
            static ref REMOTE: Regex = Regex::new(
                r#"^(git[@|://].*)|(https://.*)|(http://.*)|(ssh://.*)$"#
            ).unwrap();
        }
        LOCAL.captures(txt)
            .map(|caps| Url::Local(caps.get(1).unwrap().as_str().to_owned()))
            .or_else(|| {
                if REMOTE.is_match(txt) {
                    Some(Url::Remote(txt.to_string()))
                } else {
                    None
                }
            })
            .or_else(|| {
                GH.captures(txt)
                    .map(|caps| {
                        Url::Github(caps.get(1).unwrap().as_str().to_owned(),
                                    caps.get(2).unwrap().as_str().to_owned())
                    })
            })
    }
}

/// clone a repository at a rev to a directory
/// a best attempt effort is made to authenticate
/// requests when required to support private
/// git repositories
pub fn clone<P, R>(repo: Url, dir: P, rev: R) -> Result<()>
    where P: AsRef<Path>,
          R: Into<String>
{
    let mut cb = git2::RemoteCallbacks::new();
    let mut tried_sshkey = false;
    cb.credentials(move |url, username, cred_type| {
        if cred_type.contains(git2::USER_PASS_PLAINTEXT) {
            let cfg = git2::Config::open_default().unwrap();
            return git2::Cred::credential_helper(&cfg, url, username);
        }
        if cred_type.contains(git2::SSH_KEY) && !tried_sshkey {

            // If ssh-agent authentication fails, libgit2 will keep
            // calling this callback asking for other authentication
            // methods to try. Make sure we only try ssh-agent once,
            // to avoid looping forever.
            tried_sshkey = true;
            let username = username.unwrap();
            return git2::Cred::ssh_key_from_agent(&username);
        }
        Err(git2::Error::from_str("no authentication available"))
    });

    let mut fo = git2::FetchOptions::new();
    fo.remote_callbacks(cb).download_tags(git2::AutotagOption::All);
    let url = match repo {
        Url::Github(ref owner, ref repo) => format!("git://github.com/{}/{}.git", owner, repo),
        Url::Local(ref path) => path.to_owned(),
        Url::Remote(ref remote) => remote.to_owned(),
    };
    RepoBuilder::new().branch(&rev.into())
        .fetch_options(fo)
        .clone(&url, dir.as_ref())?;

    debug!("cloned {:?} to {:?}", repo, dir.as_ref());
    Ok(())
}

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

    #[test]
    fn test_authenticated_ssh_url() {
        assert_eq!(Url::from_str("git@github.com:user/repo.git"), Some(Url::Remote(String::from("git@github.com:user/repo.git"))))
    }

    #[test]
    fn test_public_ssh_url() {
        assert_eq!(Url::from_str("git://github.com:user/repo.git"), Some(Url::Remote(String::from("git://github.com:user/repo.git"))))
    }

    #[test]
    fn test_https_url() {
        assert_eq!(Url::from_str("https://github.com/user/repo.git"), Some(Url::Remote(String::from("https://github.com/user/repo.git"))))
    }

    #[test]
    fn test_github_url() {
        assert_eq!(Url::from_str("user/repo"), Some(Url::Github(String::from("user"), String::from("repo"))))
    }

    #[test]
    fn test_local_uri() {
        assert_eq!(Url::from_str("file:///some/path/foo.git"), Some(Url::Local(String::from("/some/path/foo.git"))))
    }
}
<|endoftext|>


<commit_before>extern crate winapi;
extern crate kernel32;

use self::super::Size;

/// Gets the current terminal size
pub fn get() -> Option<Size> {
    //http://rosettacode.org/wiki/Terminal_control/Dimensions#Windows
    use self::winapi::HANDLE;
    use self::kernel32::{GetStdHandle, GetConsoleScreenBufferInfo};
    use self::winapi::{
        CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT,
        STD_OUTPUT_HANDLE
    };
    let stdout: HANDLE = unsafe {
        GetStdHandle(STD_OUTPUT_HANDLE)
    };
    let zc = COORD { X: 0, Y: 0 };
    let mut info = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: zc.clone(),
        dwCursorPosition: zc.clone(),
        wAttributes: 0,
        srWindow: SMALL_RECT { Left:0, Top: 0, Right: 0, Bottom: 0 },
        dwMaximumWindowSize: zc
    };
    let success: bool = unsafe {
        GetConsoleScreenBufferInfo(stdout, &mut info) != 0
    };
    if success {
        Some(
            Size {
                rows: (info.srWindow.Bottom - info.srWindow.Top + 1) as u16,
                cols: (info.srWindow.Right - info.srWindow.Left + 1) as u16
            }
        )
    } else {
        None
    }
}
<commit_msg>debugging windows<commit_after>extern crate winapi;
extern crate kernel32;

use self::super::Size;

/// Gets the current terminal size
pub fn get() -> Option<Size> {
    //http://rosettacode.org/wiki/Terminal_control/Dimensions#Windows
    use self::winapi::HANDLE;
    use self::kernel32::{GetStdHandle, GetConsoleScreenBufferInfo};
    use self::winapi::{
        CONSOLE_SCREEN_BUFFER_INFO, COORD, SMALL_RECT,
        STD_OUTPUT_HANDLE
    };
    let stdout: HANDLE = unsafe {
        GetStdHandle(STD_OUTPUT_HANDLE)
    };
    let zc = COORD { X: 0, Y: 0 };
    let mut info = CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: zc.clone(),
        dwCursorPosition: zc.clone(),
        wAttributes: 0,
        srWindow: SMALL_RECT { Left:0, Top: 0, Right: 0, Bottom: 0 },
        dwMaximumWindowSize: zc
    };
    let success: bool = unsafe {
        let result = GetConsoleScreenBufferInfo(stdout, &mut info);
        println!("result was {}", result);
        result != 0
    };
    println!("info {:?}", info);
    if success {
        Some(
            Size {
                rows: (info.srWindow.Bottom - info.srWindow.Top + 1) as u16,
                cols: (info.srWindow.Right - info.srWindow.Left + 1) as u16
            }
        )
    } else {
        None
    }
}
<|endoftext|>