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

use crate::rustc::hir;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::source_map::Span;
use crate::utils::paths;
use crate::utils::{in_macro, iter_input_pats, match_type, method_chain_args, snippet, span_lint_and_then};
use if_chain::if_chain;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `option.map(f)` where f is a function
/// or closure that returns the unit type.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x: Option<&str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x: Option<&str> = do_stuff();
/// if let Some(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Some(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_clippy_lint! {
    pub OPTION_MAP_UNIT_FN,
    complexity,
    "using `option.map(f)`, where f is a function or closure that returns ()"
}

/// **What it does:** Checks for usage of `result.map(f)` where f is a function
/// or closure that returns the unit type.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x: Result<&str, &str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x: Result<&str, &str> = do_stuff();
/// if let Ok(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Ok(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_clippy_lint! {
    pub RESULT_MAP_UNIT_FN,
    complexity,
    "using `result.map(f)`, where f is a function or closure that returns ()"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN)
    }
}

fn is_unit_type(ty: ty::Ty<'_>) -> bool {
    match ty.sty {
        ty::Tuple(slice) => slice.is_empty(),
        ty::Never => true,
        _ => false,
    }
}

fn is_unit_function(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> bool {
    let ty = cx.tables.expr_ty(expr);

    if let ty::FnDef(id, _) = ty.sty {
        if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
            return is_unit_type(fn_type.output());
        }
    }
    false
}

fn is_unit_expression(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> bool {
    is_unit_type(cx.tables.expr_ty(expr))
}

/// The expression inside a closure may or may not have surrounding braces and
/// semicolons, which causes problems when generating a suggestion. Given an
/// expression that evaluates to '()' or '!', recursively remove useless braces
/// and semi-colons until is suitable for including in the suggestion template
fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr) -> Option<Span> {
    if !is_unit_expression(cx, expr) {
        return None;
    }

    match expr.node {
        hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _) => {
            // Calls can't be reduced any more
            Some(expr.span)
        },
        hir::ExprKind::Block(ref block, _) => {
            match (&block.stmts[..], block.expr.as_ref()) {
                (&[], Some(inner_expr)) => {
                    // If block only contains an expression,
                    // reduce `{ X }` to `X`
                    reduce_unit_expression(cx, inner_expr)
                },
                (&[ref inner_stmt], None) => {
                    // If block only contains statements,
                    // reduce `{ X; }` to `X` or `X;`
                    match inner_stmt.node {
                        hir::StmtKind::Decl(ref d, _) => Some(d.span),
                        hir::StmtKind::Expr(ref e, _) => Some(e.span),
                        hir::StmtKind::Semi(_, _) => Some(inner_stmt.span),
                    }
                },
                _ => {
                    // For closures that contain multiple statements
                    // it's difficult to get a correct suggestion span
                    // for all cases (multi-line closures specifically)
                    //
                    // We do not attempt to build a suggestion for those right now.
                    None
                },
            }
        },
        _ => None,
    }
}

fn unit_closure<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'a hir::Expr) -> Option<(&'tcx hir::Arg, &'a hir::Expr)> {
    if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.node {
        let body = cx.tcx.hir().body(inner_expr_id);
        let body_expr = &body.value;

        if_chain! {
            if decl.inputs.len() == 1;
            if is_unit_expression(cx, body_expr);
            if let Some(binding) = iter_input_pats(&decl, body).next();
            then {
                return Some((binding, body_expr));
            }
        }
    }
    None
}

/// Builds a name for the let binding variable (`var_arg`)
///
/// `x.field` => `x_field`
/// `y` => `_y`
///
/// Anything else will return `_`.
fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr) -> String {
    match &var_arg.node {
        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
        hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
        _ => "_".to_string(),
    }
}

fn suggestion_msg(function_type: &str, map_type: &str) -> String {
    format!(
        "called `map(f)` on an {0} value where `f` is a unit {1}",
        map_type, function_type
    )
}

fn lint_map_unit_fn(cx: &LateContext<'_, '_>, stmt: &hir::Stmt, expr: &hir::Expr, map_args: &[hir::Expr]) {
    let var_arg = &map_args[0];
    let fn_arg = &map_args[1];

    let (map_type, variant, lint) = if match_type(cx, cx.tables.expr_ty(var_arg), &paths::OPTION) {
        ("Option", "Some", OPTION_MAP_UNIT_FN)
    } else if match_type(cx, cx.tables.expr_ty(var_arg), &paths::RESULT) {
        ("Result", "Ok", RESULT_MAP_UNIT_FN)
    } else {
        return;
    };

    if is_unit_function(cx, fn_arg) {
        let msg = suggestion_msg("function", map_type);
        let suggestion = format!(
            "if let {0}({1}) = {2} {{ {3}(...) }}",
            variant,
            let_binding_name(cx, var_arg),
            snippet(cx, var_arg.span, "_"),
            snippet(cx, fn_arg.span, "_")
        );

        span_lint_and_then(cx, lint, expr.span, &msg, |db| {
            db.span_suggestion_with_applicability(stmt.span, "try this", suggestion, Applicability::Unspecified);
        });
    } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
        let msg = suggestion_msg("closure", map_type);

        span_lint_and_then(cx, lint, expr.span, &msg, |db| {
            if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
                let suggestion = format!(
                    "if let {0}({1}) = {2} {{ {3} }}",
                    variant,
                    snippet(cx, binding.pat.span, "_"),
                    snippet(cx, var_arg.span, "_"),
                    snippet(cx, reduced_expr_span, "_")
                );
                db.span_suggestion_with_applicability(
                    stmt.span,
                    "try this",
                    suggestion,
                    Applicability::MachineApplicable, // snippet
                );
            } else {
                let suggestion = format!(
                    "if let {0}({1}) = {2} {{ ... }}",
                    variant,
                    snippet(cx, binding.pat.span, "_"),
                    snippet(cx, var_arg.span, "_")
                );
                db.span_suggestion_with_applicability(stmt.span, "try this", suggestion, Applicability::Unspecified);
            }
        });
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt) {
        if in_macro(stmt.span) {
            return;
        }

        if let hir::StmtKind::Semi(ref expr, _) = stmt.node {
            if let Some(arglists) = method_chain_args(expr, &["map"]) {
                lint_map_unit_fn(cx, stmt, expr, arglists[0]);
            }
        }
    }
}
<commit_msg>panic at map_unit_fn.rs:202 for map() without args<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::ty;
use crate::rustc::{declare_tool_lint, lint_array};
use crate::rustc_errors::Applicability;
use crate::syntax::source_map::Span;
use crate::utils::paths;
use crate::utils::{in_macro, iter_input_pats, match_type, method_chain_args, snippet, span_lint_and_then};
use if_chain::if_chain;

#[derive(Clone)]
pub struct Pass;

/// **What it does:** Checks for usage of `option.map(f)` where f is a function
/// or closure that returns the unit type.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x: Option<&str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x: Option<&str> = do_stuff();
/// if let Some(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Some(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_clippy_lint! {
    pub OPTION_MAP_UNIT_FN,
    complexity,
    "using `option.map(f)`, where f is a function or closure that returns ()"
}

/// **What it does:** Checks for usage of `result.map(f)` where f is a function
/// or closure that returns the unit type.
///
/// **Why is this bad?** Readability, this can be written more clearly with
/// an if let statement
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// let x: Result<&str, &str> = do_stuff();
/// x.map(log_err_msg);
/// x.map(|msg| log_err_msg(format_msg(msg)))
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x: Result<&str, &str> = do_stuff();
/// if let Ok(msg) = x {
///     log_err_msg(msg)
/// }
/// if let Ok(msg) = x {
///     log_err_msg(format_msg(msg))
/// }
/// ```
declare_clippy_lint! {
    pub RESULT_MAP_UNIT_FN,
    complexity,
    "using `result.map(f)`, where f is a function or closure that returns ()"
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN)
    }
}

fn is_unit_type(ty: ty::Ty<'_>) -> bool {
    match ty.sty {
        ty::Tuple(slice) => slice.is_empty(),
        ty::Never => true,
        _ => false,
    }
}

fn is_unit_function(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> bool {
    let ty = cx.tables.expr_ty(expr);

    if let ty::FnDef(id, _) = ty.sty {
        if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
            return is_unit_type(fn_type.output());
        }
    }
    false
}

fn is_unit_expression(cx: &LateContext<'_, '_>, expr: &hir::Expr) -> bool {
    is_unit_type(cx.tables.expr_ty(expr))
}

/// The expression inside a closure may or may not have surrounding braces and
/// semicolons, which causes problems when generating a suggestion. Given an
/// expression that evaluates to '()' or '!', recursively remove useless braces
/// and semi-colons until is suitable for including in the suggestion template
fn reduce_unit_expression<'a>(cx: &LateContext<'_, '_>, expr: &'a hir::Expr) -> Option<Span> {
    if !is_unit_expression(cx, expr) {
        return None;
    }

    match expr.node {
        hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(_, _, _) => {
            // Calls can't be reduced any more
            Some(expr.span)
        },
        hir::ExprKind::Block(ref block, _) => {
            match (&block.stmts[..], block.expr.as_ref()) {
                (&[], Some(inner_expr)) => {
                    // If block only contains an expression,
                    // reduce `{ X }` to `X`
                    reduce_unit_expression(cx, inner_expr)
                },
                (&[ref inner_stmt], None) => {
                    // If block only contains statements,
                    // reduce `{ X; }` to `X` or `X;`
                    match inner_stmt.node {
                        hir::StmtKind::Decl(ref d, _) => Some(d.span),
                        hir::StmtKind::Expr(ref e, _) => Some(e.span),
                        hir::StmtKind::Semi(_, _) => Some(inner_stmt.span),
                    }
                },
                _ => {
                    // For closures that contain multiple statements
                    // it's difficult to get a correct suggestion span
                    // for all cases (multi-line closures specifically)
                    //
                    // We do not attempt to build a suggestion for those right now.
                    None
                },
            }
        },
        _ => None,
    }
}

fn unit_closure<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'a hir::Expr) -> Option<(&'tcx hir::Arg, &'a hir::Expr)> {
    if let hir::ExprKind::Closure(_, ref decl, inner_expr_id, _, _) = expr.node {
        let body = cx.tcx.hir().body(inner_expr_id);
        let body_expr = &body.value;

        if_chain! {
            if decl.inputs.len() == 1;
            if is_unit_expression(cx, body_expr);
            if let Some(binding) = iter_input_pats(&decl, body).next();
            then {
                return Some((binding, body_expr));
            }
        }
    }
    None
}

/// Builds a name for the let binding variable (`var_arg`)
///
/// `x.field` => `x_field`
/// `y` => `_y`
///
/// Anything else will return `_`.
fn let_binding_name(cx: &LateContext<'_, '_>, var_arg: &hir::Expr) -> String {
    match &var_arg.node {
        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
        hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
        _ => "_".to_string(),
    }
}

fn suggestion_msg(function_type: &str, map_type: &str) -> String {
    format!(
        "called `map(f)` on an {0} value where `f` is a unit {1}",
        map_type, function_type
    )
}

fn lint_map_unit_fn(cx: &LateContext<'_, '_>, stmt: &hir::Stmt, expr: &hir::Expr, map_args: &[hir::Expr]) {
    let var_arg = &map_args[0];

    let (map_type, variant, lint) = if match_type(cx, cx.tables.expr_ty(var_arg), &paths::OPTION) {
        ("Option", "Some", OPTION_MAP_UNIT_FN)
    } else if match_type(cx, cx.tables.expr_ty(var_arg), &paths::RESULT) {
        ("Result", "Ok", RESULT_MAP_UNIT_FN)
    } else {
        return;
    };
    let fn_arg = &map_args[1];

    if is_unit_function(cx, fn_arg) {
        let msg = suggestion_msg("function", map_type);
        let suggestion = format!(
            "if let {0}({1}) = {2} {{ {3}(...) }}",
            variant,
            let_binding_name(cx, var_arg),
            snippet(cx, var_arg.span, "_"),
            snippet(cx, fn_arg.span, "_")
        );

        span_lint_and_then(cx, lint, expr.span, &msg, |db| {
            db.span_suggestion_with_applicability(stmt.span, "try this", suggestion, Applicability::Unspecified);
        });
    } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
        let msg = suggestion_msg("closure", map_type);

        span_lint_and_then(cx, lint, expr.span, &msg, |db| {
            if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
                let suggestion = format!(
                    "if let {0}({1}) = {2} {{ {3} }}",
                    variant,
                    snippet(cx, binding.pat.span, "_"),
                    snippet(cx, var_arg.span, "_"),
                    snippet(cx, reduced_expr_span, "_")
                );
                db.span_suggestion_with_applicability(
                    stmt.span,
                    "try this",
                    suggestion,
                    Applicability::MachineApplicable, // snippet
                );
            } else {
                let suggestion = format!(
                    "if let {0}({1}) = {2} {{ ... }}",
                    variant,
                    snippet(cx, binding.pat.span, "_"),
                    snippet(cx, var_arg.span, "_")
                );
                db.span_suggestion_with_applicability(stmt.span, "try this", suggestion, Applicability::Unspecified);
            }
        });
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt) {
        if in_macro(stmt.span) {
            return;
        }

        if let hir::StmtKind::Semi(ref expr, _) = stmt.node {
            if let Some(arglists) = method_chain_args(expr, &["map"]) {
                lint_map_unit_fn(cx, stmt, expr, arglists[0]);
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Asynchronous notifications.

use std::fmt;
use std::time::Duration;

use {desynchronized, Result, Connection, NotificationsNew};
use message::BackendMessage::NotificationResponse;
use error::Error;

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    /// The process ID of the notifying backend process.
    pub pid: u32,
    /// The name of the channel that the notify has been raised on.
    pub channel: String,
    /// The "payload" string passed from the notifying process.
    pub payload: String,
}

/// Notifications from the Postgres backend.
pub struct Notifications<'conn> {
    conn: &'conn Connection,
}

impl<'a> fmt::Debug for Notifications<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Notifications")
           .field("pending", &self.len())
           .finish()
    }
}

impl<'conn> Notifications<'conn> {
    /// Returns the number of pending notifications.
    pub fn len(&self) -> usize {
        self.conn.conn.borrow().notifications.len()
    }

    /// Returns an iterator over pending notifications.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks until one is
    /// received if none are pending.
    ///
    /// The iterator will never return `None`.
    pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
        BlockingIter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks for a limited time
    /// waiting to receive one if none are pending.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> {
        TimeoutIter {
            conn: self.conn,
            timeout: timeout,
        }
    }
}

impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
    type Item = Result<Notification>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

impl<'conn> NotificationsNew<'conn> for Notifications<'conn> {
    fn new(conn: &'conn Connection) -> Notifications<'conn> {
        Notifications { conn: conn }
    }
}

/// An iterator over pending notifications.
pub struct Iter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        match conn.read_message_with_notification_nonblocking() {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}

/// An iterator over notifications which will block if none are pending.
pub struct BlockingIter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for BlockingIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification() {
            Ok(NotificationResponse { pid, channel, payload }) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::max_value(), None)
    }
}

/// An iterator over notifications which will block for a period of time if
/// none are pending.
pub struct TimeoutIter<'a> {
    conn: &'a Connection,
    timeout: Duration,
}

impl<'a> Iterator for TimeoutIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_timeout(self.timeout) {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}
<commit_msg>Add desync check to notifications iter<commit_after>//! Asynchronous notifications.

use std::fmt;
use std::time::Duration;

use {desynchronized, Result, Connection, NotificationsNew};
use message::BackendMessage::NotificationResponse;
use error::Error;

/// An asynchronous notification.
#[derive(Clone, Debug)]
pub struct Notification {
    /// The process ID of the notifying backend process.
    pub pid: u32,
    /// The name of the channel that the notify has been raised on.
    pub channel: String,
    /// The "payload" string passed from the notifying process.
    pub payload: String,
}

/// Notifications from the Postgres backend.
pub struct Notifications<'conn> {
    conn: &'conn Connection,
}

impl<'a> fmt::Debug for Notifications<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.debug_struct("Notifications")
           .field("pending", &self.len())
           .finish()
    }
}

impl<'conn> Notifications<'conn> {
    /// Returns the number of pending notifications.
    pub fn len(&self) -> usize {
        self.conn.conn.borrow().notifications.len()
    }

    /// Returns an iterator over pending notifications.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks until one is
    /// received if none are pending.
    ///
    /// The iterator will never return `None`.
    pub fn blocking_iter<'a>(&'a self) -> BlockingIter<'a> {
        BlockingIter { conn: self.conn }
    }

    /// Returns an iterator over notifications that blocks for a limited time
    /// waiting to receive one if none are pending.
    ///
    /// # Note
    ///
    /// This iterator may start returning `Some` after previously returning
    /// `None` if more notifications are received.
    pub fn timeout_iter<'a>(&'a self, timeout: Duration) -> TimeoutIter<'a> {
        TimeoutIter {
            conn: self.conn,
            timeout: timeout,
        }
    }
}

impl<'a, 'conn> IntoIterator for &'a Notifications<'conn> {
    type Item = Result<Notification>;
    type IntoIter = Iter<'a>;

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

impl<'conn> NotificationsNew<'conn> for Notifications<'conn> {
    fn new(conn: &'conn Connection) -> Notifications<'conn> {
        Notifications { conn: conn }
    }
}

/// An iterator over pending notifications.
pub struct Iter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for Iter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_nonblocking() {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}

/// An iterator over notifications which will block if none are pending.
pub struct BlockingIter<'a> {
    conn: &'a Connection,
}

impl<'a> Iterator for BlockingIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification() {
            Ok(NotificationResponse { pid, channel, payload }) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::max_value(), None)
    }
}

/// An iterator over notifications which will block for a period of time if
/// none are pending.
pub struct TimeoutIter<'a> {
    conn: &'a Connection,
    timeout: Duration,
}

impl<'a> Iterator for TimeoutIter<'a> {
    type Item = Result<Notification>;

    fn next(&mut self) -> Option<Result<Notification>> {
        let mut conn = self.conn.conn.borrow_mut();

        if let Some(notification) = conn.notifications.pop_front() {
            return Some(Ok(notification));
        }

        if conn.is_desynchronized() {
            return Some(Err(Error::Io(desynchronized())));
        }

        match conn.read_message_with_notification_timeout(self.timeout) {
            Ok(Some(NotificationResponse { pid, channel, payload })) => {
                Some(Ok(Notification {
                    pid: pid,
                    channel: channel,
                    payload: payload,
                }))
            }
            Ok(None) => None,
            Err(err) => Some(Err(Error::Io(err))),
            _ => unreachable!(),
        }
    }
}
<|endoftext|>


<commit_before>//! Updates crates.io index and builds new packages

use super::{DocBuilder, RustwideBuilder};
use crate::db::connect_db;
use crate::error::Result;
use crate::utils::{add_crate_to_queue, get_crate_priority};
use crates_index_diff::{ChangeKind, Index};
use log::{debug, error};

impl DocBuilder {
    /// Updates crates.io-index repository and adds new crates into build queue.
    /// Returns size of queue
    pub fn get_new_crates(&mut self) -> Result<usize> {
        let conn = connect_db()?;
        let index = Index::from_path_or_cloned(&self.options.crates_io_index_path)?;
        let mut changes = index.fetch_changes()?;
        let mut add_count: usize = 0;

        // I belive this will fix ordering of queue if we get more than one crate from changes
        changes.reverse();

        for krate in changes.iter().filter(|k| k.kind != ChangeKind::Yanked) {
            let priority = get_crate_priority(&conn, &krate.name)?;
            add_crate_to_queue(&conn, &krate.name, &krate.version, priority).ok();

            debug!("{}-{} added into build queue", krate.name, krate.version);
            add_count += 1;
        }

        Ok(add_count)
    }

    pub fn get_queue_count(&self) -> Result<i64> {
        let conn = connect_db()?;

        Ok(conn
            .query("SELECT COUNT(*) FROM queue WHERE attempt < 5", &[])?
            .get(0)
            .get(0))
    }

    /// Builds the top package from the queue. Returns whether the queue was empty.
    pub(crate) fn build_next_queue_package(
        &mut self,
        builder: &mut RustwideBuilder,
    ) -> Result<bool> {
        let conn = connect_db()?;

        let query = conn.query(
            "SELECT id, name, version
                                     FROM queue
                                     WHERE attempt < 5
                                     ORDER BY priority ASC, attempt ASC, id ASC
                                     LIMIT 1",
            &[],
        )?;

        if query.is_empty() {
            // nothing in the queue; bail
            return Ok(false);
        }

        let id: i32 = query.get(0).get(0);
        let name: String = query.get(0).get(1);
        let version: String = query.get(0).get(2);

        match builder.build_package(self, &name, &version, None) {
            Ok(_) => {
                let _ = conn.execute("DELETE FROM queue WHERE id = $1", &[&id]);
                crate::web::metrics::TOTAL_BUILDS.inc();
            }
            Err(e) => {
                // Increase attempt count
                let rows = conn.query(
                    "UPDATE queue SET attempt = attempt + 1 WHERE id = $1 RETURNING attempt",
                    &[&id],
                )?;
                let attempt: i32 = rows.get(0).get(0);
                if attempt >= 5 {
                    crate::web::metrics::FAILED_BUILDS.inc();
                    crate::web::metrics::TOTAL_BUILDS.inc();
                }
                error!(
                    "Failed to build package {}-{} from queue: {}",
                    name, version, e
                )
            }
        }

        Ok(true)
    }
}

#[cfg(test)]
mod test {
    use crate::{DocBuilder, DocBuilderOptions};
    use log::error;
    use std::path::PathBuf;

    #[test]
    #[ignore]
    fn test_get_new_crates() {
        let _ = env_logger::try_init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let mut docbuilder = DocBuilder::new(options);
        let res = docbuilder.get_new_crates();
        if res.is_err() {
            error!("{:?}", res);
        }
        assert!(res.is_ok());
    }
}
<commit_msg>Added some fault-tolerance to the build queue<commit_after>//! Updates crates.io index and builds new packages

use super::{DocBuilder, RustwideBuilder};
use crate::db::connect_db;
use crate::error::Result;
use crate::utils::{add_crate_to_queue, get_crate_priority};
use crates_index_diff::{ChangeKind, Index};
use log::{debug, error};

impl DocBuilder {
    /// Updates crates.io-index repository and adds new crates into build queue.
    /// Returns size of queue
    pub fn get_new_crates(&mut self) -> Result<usize> {
        let conn = connect_db()?;
        let index = Index::from_path_or_cloned(&self.options.crates_io_index_path)?;
        let (mut changes, oid) = index.peek_changes()?;
        let mut add_count: usize = 0;

        // I believe this will fix ordering of queue if we get more than one crate from changes
        changes.reverse();

        for krate in changes.iter().filter(|k| k.kind != ChangeKind::Yanked) {
            let priority = get_crate_priority(&conn, &krate.name)?;
            add_crate_to_queue(&conn, &krate.name, &krate.version, priority).ok();

            debug!("{}-{} added into build queue", krate.name, krate.version);
            add_count += 1;
        }

        index.set_last_seen_reference(oid)?;

        Ok(add_count)
    }

    pub fn get_queue_count(&self) -> Result<i64> {
        let conn = connect_db()?;

        Ok(conn
            .query("SELECT COUNT(*) FROM queue WHERE attempt < 5", &[])?
            .get(0)
            .get(0))
    }

    /// Builds the top package from the queue. Returns whether the queue was empty.
    pub(crate) fn build_next_queue_package(
        &mut self,
        builder: &mut RustwideBuilder,
    ) -> Result<bool> {
        let conn = connect_db()?;

        let query = conn.query(
            "SELECT id, name, version
                                     FROM queue
                                     WHERE attempt < 5
                                     ORDER BY priority ASC, attempt ASC, id ASC
                                     LIMIT 1",
            &[],
        )?;

        if query.is_empty() {
            // nothing in the queue; bail
            return Ok(false);
        }

        let id: i32 = query.get(0).get(0);
        let name: String = query.get(0).get(1);
        let version: String = query.get(0).get(2);

        match builder.build_package(self, &name, &version, None) {
            Ok(_) => {
                let _ = conn.execute("DELETE FROM queue WHERE id = $1", &[&id]);
                crate::web::metrics::TOTAL_BUILDS.inc();
            }
            Err(e) => {
                // Increase attempt count
                let rows = conn.query(
                    "UPDATE queue SET attempt = attempt + 1 WHERE id = $1 RETURNING attempt",
                    &[&id],
                )?;
                let attempt: i32 = rows.get(0).get(0);
                if attempt >= 5 {
                    crate::web::metrics::FAILED_BUILDS.inc();
                    crate::web::metrics::TOTAL_BUILDS.inc();
                }
                error!(
                    "Failed to build package {}-{} from queue: {}",
                    name, version, e
                )
            }
        }

        Ok(true)
    }
}

#[cfg(test)]
mod test {
    use crate::{DocBuilder, DocBuilderOptions};
    use log::error;
    use std::path::PathBuf;

    #[test]
    #[ignore]
    fn test_get_new_crates() {
        let _ = env_logger::try_init();
        let options = DocBuilderOptions::from_prefix(PathBuf::from("../cratesfyi-prefix"));
        let mut docbuilder = DocBuilder::new(options);
        let res = docbuilder.get_new_crates();
        if res.is_err() {
            error!("{:?}", res);
        }
        assert!(res.is_ok());
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use users::UsersCache;

use style::Colours;
use output::cell::TextCell;
use output::time::TimeFormat;
use fs::{File, Dir, fields as f};
use fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub extra_columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "<table options>")
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        columns.push(Column::Permissions);

        if self.links {
            columns.push(Column::HardLinks);
        }

        columns.push(Column::FileSize);

        if self.blocks {
            columns.push(Column::Blocks);
        }

        columns.push(Column::User);

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s creation time (`st_ctime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(&self) -> &'static str {
        match *self {
            TimeType::Accessed  => "Date Accessed",
            TimeType::Modified  => "Date Modified",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub accessed: bool,
    pub modified: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { accessed: false, modified: true, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    TimeZone::from_file("/etc/localtime")
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, dir: Option<&'a Dir>, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let has_git = if let (Some(g), Some(d)) = (git, dir) { g.has_anything_for(&d.path) } else { false };
        let columns = options.extra_columns.collect(has_git);
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs: xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref<'a>(&'a self) -> &'a Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<commit_msg>Fix TableOptions Debug<commit_after>use std::cmp::max;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use users::UsersCache;

use style::Colours;
use output::cell::TextCell;
use output::time::TimeFormat;
use fs::{File, Dir, fields as f};
use fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub extra_columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Table({:#?})", self.extra_columns)
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        columns.push(Column::Permissions);

        if self.links {
            columns.push(Column::HardLinks);
        }

        columns.push(Column::FileSize);

        if self.blocks {
            columns.push(Column::Blocks);
        }

        columns.push(Column::User);

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s creation time (`st_ctime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(&self) -> &'static str {
        match *self {
            TimeType::Accessed  => "Date Accessed",
            TimeType::Modified  => "Date Modified",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub accessed: bool,
    pub modified: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { accessed: false, modified: true, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    TimeZone::from_file("/etc/localtime")
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, dir: Option<&'a Dir>, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let has_git = if let (Some(g), Some(d)) = (git, dir) { g.has_anything_for(&d.path) } else { false };
        let columns = options.extra_columns.collect(has_git);
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs: xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref<'a>(&'a self) -> &'a Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::error::Error;

use deadpool_postgres::Client;

use crate::data_types::AlleleShortMap;
use crate::db::Processed;
use crate::types::OrganismTaxonId;

pub struct Loader {
    conn: Client,
    taxonid: OrganismTaxonId,
    processed: Processed,
}

impl Loader {
    pub fn new(conn: Client, taxonid: OrganismTaxonId, processed: Processed) -> Loader {
        Loader {
            conn,
            taxonid,
            processed,
        }
    }

    pub async fn load_alleles(&mut self, alleles: &AlleleShortMap) -> Result<(), Box<dyn Error>>
    {
        let organism = self.processed.organism_by_taxonid(self.taxonid)
            .expect(&format!("load failed, organism not in database with taxon ID: {}", self.taxonid));

        let trans = self.conn.transaction().await?;

        let allele_insert_sql = "INSERT INTO feature(uniquename, name, type_id, organism_id)
VALUES ($1, $2,
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = 'allele' AND cv.name = 'sequence' LIMIT 1),
        (SELECT organism_id FROM organism WHERE genus = $3 AND species = $4 LIMIT 1))";

        let allele_smt = trans.prepare(allele_insert_sql).await?;

        let rel_sql = "INSERT INTO feature_relationship(subject_id, object_id, type_id)
VALUES ((SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE t.name = 'allele' AND f.uniquename = $1),
        (SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE (t.name = 'gene' OR t.name = 'pseudogene') AND f.uniquename = $2),
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = 'instance_of' AND cv.name = 'pombase_relations' LIMIT 1))";

        let rel_smt = trans.prepare(rel_sql).await?;

        let add_feat_prop_sql = "INSERT INTO featureprop(feature_id, type_id, value)
VALUES ((SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE t.name = 'allele' AND f.uniquename = $1),
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = $2 AND cv.name = 'PomBase feature property types' LIMIT 1),
        $3)";

        let add_feat_prop_smt = trans.prepare(add_feat_prop_sql).await?;

        let add_synonym_sql =
            "INSERT INTO synonym (name, type_id, synonym_sgml)
                                   VALUES ($1, (SELECT cvterm_id FROM cvterm t JOIN cv ON cv.cv_id = t.cv_id
                                                 WHERE t.name = 'exact' AND cv.name = 'synonym_type'), '')
                              ON CONFLICT DO NOTHING";

        let add_synonym_smt = trans.prepare(add_synonym_sql).await?;

        let add_pub_sql =
            "INSERT INTO pub (uniquename, type_id)
                         VALUES ($1, (SELECT cvterm_id FROM cvterm WHERE name = 'paper'))
                        ON CONFLICT DO NOTHING";

        let add_pub_smt = trans.prepare(add_pub_sql).await?;

        let add_feat_synonym_sql =
            "INSERT INTO feature_synonym (feature_id, synonym_id, pub_id)
                 SELECT (SELECT feature_id FROM feature f WHERE f.uniquename = $1),
                        (SELECT synonym_id FROM synonym
                              JOIN cvterm t ON t.cvterm_id = synonym.type_id
                              JOIN cv ON cv.cv_id = t.cv_id
                             WHERE synonym.name = $2 AND t.name = 'exact' AND cv.name = 'synonym_type'),
                        (SELECT pub_id FROM pub WHERE uniquename = $3)";

        let add_feat_synonym_smt = trans.prepare(add_feat_synonym_sql).await?;

        let mut seen_allele_names = HashSet::new();

        for allele in alleles.values() {
            if let Some(ref allele_name) = allele.name {
                if seen_allele_names.contains(allele_name) {
                    eprintln!("skipping allele with name that has alread been loaded: {} {}",
                              allele_name.as_str(),
                              allele.allele_type);
                    continue;
                } else {
                    seen_allele_names.insert(allele_name.clone());
               }
            }

            if self.processed.allele_by_uniquename(&allele.uniquename).is_some() {
                panic!("allele loading failed: can't find {} in the database", allele.uniquename);
            }

            if self.processed.gene_by_uniquename(&allele.gene_uniquename).is_none() {
                panic!("allele loading failed: can't find {} in the database", allele.gene_uniquename);
            }

            let allele_uniquename = allele.uniquename.as_str();
            let allele_name = allele.name.as_ref().map(|s| s.as_str());
            let genus = organism.genus.as_str();
            let species = organism.species.as_str();
            let gene_uniquename = allele.gene_uniquename.as_str();

            trans.query(&allele_smt,
                        &[&allele_uniquename, &allele_name, &genus, &species]).await?;

            trans.query(&rel_smt, &[&allele_uniquename, &gene_uniquename]).await?;

            let allele_type = allele.allele_type.as_str();
            trans.query(&add_feat_prop_smt, &[&allele_uniquename, &"allele_type", &allele_type]).await?;

            let allele_description = allele.description.as_ref().map(|s| s.as_str());
            trans.query(&add_feat_prop_smt, &[&allele_uniquename, &"description", &allele_description]).await?;

            for synonym in &allele.synonyms {
              let synonym_name = synonym.name.as_str();

              trans.query(&add_synonym_smt, &[&synonym_name]).await?;

              let pub_uniquename =
                  if let Some(ref uniquename) = synonym.reference {
                    uniquename.as_str()
                  } else {
                    "null"
                  };

              trans.query(&add_pub_smt, &[&pub_uniquename]).await?;

              trans.query(&add_feat_synonym_smt, &[&allele_uniquename, &synonym_name, &pub_uniquename]).await?;
            }
        }

        trans.commit().await?;

        Ok(())
    }
}
<commit_msg>Fix error messages from pombase-chado-load<commit_after>use std::collections::HashSet;
use std::error::Error;

use deadpool_postgres::Client;

use crate::data_types::AlleleShortMap;
use crate::db::Processed;
use crate::types::OrganismTaxonId;

pub struct Loader {
    conn: Client,
    taxonid: OrganismTaxonId,
    processed: Processed,
}

impl Loader {
    pub fn new(conn: Client, taxonid: OrganismTaxonId, processed: Processed) -> Loader {
        Loader {
            conn,
            taxonid,
            processed,
        }
    }

    pub async fn load_alleles(&mut self, alleles: &AlleleShortMap) -> Result<(), Box<dyn Error>>
    {
        let organism = self.processed.organism_by_taxonid(self.taxonid)
            .expect(&format!("load failed, organism not in database with taxon ID: {}", self.taxonid));

        let trans = self.conn.transaction().await?;

        let allele_insert_sql = "INSERT INTO feature(uniquename, name, type_id, organism_id)
VALUES ($1, $2,
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = 'allele' AND cv.name = 'sequence' LIMIT 1),
        (SELECT organism_id FROM organism WHERE genus = $3 AND species = $4 LIMIT 1))";

        let allele_smt = trans.prepare(allele_insert_sql).await?;

        let rel_sql = "INSERT INTO feature_relationship(subject_id, object_id, type_id)
VALUES ((SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE t.name = 'allele' AND f.uniquename = $1),
        (SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE (t.name = 'gene' OR t.name = 'pseudogene') AND f.uniquename = $2),
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = 'instance_of' AND cv.name = 'pombase_relations' LIMIT 1))";

        let rel_smt = trans.prepare(rel_sql).await?;

        let add_feat_prop_sql = "INSERT INTO featureprop(feature_id, type_id, value)
VALUES ((SELECT feature_id FROM feature f join cvterm t ON f.type_id = t.cvterm_id WHERE t.name = 'allele' AND f.uniquename = $1),
        (SELECT cvterm_id FROM cvterm t join cv ON cv.cv_id = t.cv_id WHERE t.name = $2 AND cv.name = 'PomBase feature property types' LIMIT 1),
        $3)";

        let add_feat_prop_smt = trans.prepare(add_feat_prop_sql).await?;

        let add_synonym_sql =
            "INSERT INTO synonym (name, type_id, synonym_sgml)
                                   VALUES ($1, (SELECT cvterm_id FROM cvterm t JOIN cv ON cv.cv_id = t.cv_id
                                                 WHERE t.name = 'exact' AND cv.name = 'synonym_type'), '')
                              ON CONFLICT DO NOTHING";

        let add_synonym_smt = trans.prepare(add_synonym_sql).await?;

        let add_pub_sql =
            "INSERT INTO pub (uniquename, type_id)
                         VALUES ($1, (SELECT cvterm_id FROM cvterm WHERE name = 'paper'))
                        ON CONFLICT DO NOTHING";

        let add_pub_smt = trans.prepare(add_pub_sql).await?;

        let add_feat_synonym_sql =
            "INSERT INTO feature_synonym (feature_id, synonym_id, pub_id)
                 SELECT (SELECT feature_id FROM feature f WHERE f.uniquename = $1),
                        (SELECT synonym_id FROM synonym
                              JOIN cvterm t ON t.cvterm_id = synonym.type_id
                              JOIN cv ON cv.cv_id = t.cv_id
                             WHERE synonym.name = $2 AND t.name = 'exact' AND cv.name = 'synonym_type'),
                        (SELECT pub_id FROM pub WHERE uniquename = $3)";

        let add_feat_synonym_smt = trans.prepare(add_feat_synonym_sql).await?;

        let mut seen_allele_names = HashSet::new();

        for allele in alleles.values() {
            if let Some(ref allele_name) = allele.name {
                if seen_allele_names.contains(allele_name) {
                    eprintln!("skipping allele with name that has already been loaded: {} {}",
                              allele_name.as_str(),
                              allele.allele_type);
                    continue;
                } else {
                    seen_allele_names.insert(allele_name.clone());
               }
            }

            if self.processed.allele_by_uniquename(&allele.uniquename).is_some() {
                panic!("allele loading failed: {} is already in the database", allele.uniquename);
            }

            if self.processed.gene_by_uniquename(&allele.gene_uniquename).is_none() {
                panic!("allele loading failed: can't find {} in the database", allele.gene_uniquename);
            }

            let allele_uniquename = allele.uniquename.as_str();
            let allele_name = allele.name.as_ref().map(|s| s.as_str());
            let genus = organism.genus.as_str();
            let species = organism.species.as_str();
            let gene_uniquename = allele.gene_uniquename.as_str();

            trans.query(&allele_smt,
                        &[&allele_uniquename, &allele_name, &genus, &species]).await?;

            trans.query(&rel_smt, &[&allele_uniquename, &gene_uniquename]).await?;

            let allele_type = allele.allele_type.as_str();
            trans.query(&add_feat_prop_smt, &[&allele_uniquename, &"allele_type", &allele_type]).await?;

            let allele_description = allele.description.as_ref().map(|s| s.as_str());
            trans.query(&add_feat_prop_smt, &[&allele_uniquename, &"description", &allele_description]).await?;

            for synonym in &allele.synonyms {
              let synonym_name = synonym.name.as_str();

              trans.query(&add_synonym_smt, &[&synonym_name]).await?;

              let pub_uniquename =
                  if let Some(ref uniquename) = synonym.reference {
                    uniquename.as_str()
                  } else {
                    "null"
                  };

              trans.query(&add_pub_smt, &[&pub_uniquename]).await?;

              trans.query(&add_feat_synonym_smt, &[&allele_uniquename, &synonym_name, &pub_uniquename]).await?;
            }
        }

        trans.commit().await?;

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


<commit_before>#![feature(asm)]

// External libraries
extern crate dylib;
use dylib::DynamicLibrary;

use std::path::Path;
use std::mem::transmute;

use std::process::Command;
use std::thread;

use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
use std::option::Option;

use std::sync::{Arc, RwLock};
use std::fmt;
use std::io::Write;

// Internal libraries
extern crate gr;
use gr::*;

#[derive(Clone)]
enum OpType {
    Func(String),
    Const(usize),
    Var(usize),
    UVar(usize),
    Op(String),
    Pow(i32)
}

impl fmt::Display for OpType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let outstring = match self {
            &OpType::Func(ref s) => format!("f{}", s),
            &OpType::Const(i) => format!("c{}", i),
            &OpType::Var(i) => format!("i{}", i),
            &OpType::UVar(i) => format!("v{}", i),
            &OpType::Op(ref s) => format!("o{}", s),
            &OpType::Pow(i) => format!("p{}", i)
        };
        write!(f, "{}", outstring)
    }

}

#[derive(Clone)]
pub struct FuncObj {
    handle: Arc<RwLock<Option<DynamicLibrary>>>,
    user_vars: Vec<GI>,
    constants: Vec<GI>,
    instructions: Vec<OpType>,
    switched: Arc<AtomicBool>,
    function: Arc<AtomicPtr<fn(&Vec<GI>, &Vec<GI>) -> GI>>,

}

unsafe impl Sync for FuncObj {}
unsafe impl Send for FuncObj {}
fn dummy(_x: &Vec<GI>, _c: &Vec<GI>) -> GI {
    GI::new_c("1.0").unwrap()
}

impl FuncObj {
    pub fn call(&self, _x: &Vec<GI>) -> GI {
        if self.switched.load(Ordering::Acquire) {
            let real_func = unsafe{
                std::mem::transmute::<*mut fn(&Vec<GI>, &Vec<GI>)->GI,
                                      fn(&Vec<GI>, &Vec<GI>)->GI>(
                    (self.function.load(Ordering::Acquire)))};
            real_func(_x, &self.constants)
        }
        else {
            self.interpreted(_x, &self.constants)
        }
    }

    fn set(&self, f: fn(&Vec<GI>,&Vec<GI>) -> GI, handle: DynamicLibrary) {
        // This FuncObj owns the handle from the dynamic lib as the lifetime of
        // f is tied to the lifetime of the handle.
        self.function.store(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                         *mut fn(&Vec<GI>, &Vec<GI>)->GI>(f)}, Ordering::Release);
        let mut h = self.handle.write().unwrap();
        *h = Some(handle);
        self.switched.store(true, Ordering::Release);
    }

    fn interpreted(&self, _x: &Vec<GI>, _c: &Vec<GI>) -> GI {
        let mut stack: Vec<GI> = Vec::new();
        for inst in &self.instructions {
            match inst {
                &OpType::Func(ref s) => {
                    let op = stack.last_mut().unwrap();
                    match s.as_str() {
                        "abs" => op.abs(),
                        "sin" => op.sin(),
                        "cos" => op.cos(),
                        "tan" => op.tan(),
                        "exp" => op.exp(),
                        "log" => op.log(),
                        "neg" => op.neg(),
                        "sqrt" => op.sqrt(),
                        _     => unreachable!()
                    };
                },
                &OpType::Const(i) => {
                    stack.push(self.constants[i]);
                },
                &OpType::Var(i) => {
                    stack.push(_x[i]);
                },
                &OpType::UVar(i) => {
                    stack.push(self.user_vars[i]);
                },
                &OpType::Op(ref s) => {
                    let right = stack.pop().unwrap();
                    let left = stack.last_mut().unwrap();
                    match s.as_str() {
                        "+" => left.add(right),
                        "-" => left.sub(right),
                        "*" => left.mul(right),
                        "/" => left.div(right),
                        "p" => left.powi(right),
                        _   => unreachable!()
                    };
                },
                &OpType::Pow(exp) => {
                    let arg = stack.last_mut().unwrap();
                    arg.pow(exp);
                }
            }
        }
        stack[0]
    }

    pub fn new(consts: &Vec<GI>, instructions: &String, debug: bool, suffix: String) -> FuncObj {
        let mut insts = vec![];

        for inst in instructions.split(',') {
            let dummy = inst.trim().to_string();
            let (first, rest) = dummy.split_at(1);
            insts.push(match first {
                "c" => OpType::Const(rest.to_string().parse::<usize>().unwrap()),
                "i" => OpType::Var(rest.to_string().parse::<usize>().unwrap()),
                "v" => OpType::UVar(rest.to_string().parse::<usize>().unwrap()),
                "o" => OpType::Op(rest.to_string()),
                "f" => OpType::Func(rest.to_string()),
                "p" => OpType::Pow(rest.to_string().parse::<i32>().unwrap()),
                _   => panic!()
            });
        }
        let result =
            FuncObj{handle: Arc::new(RwLock::new(Option::None)),
                user_vars: vec![],
                constants: consts.clone(),
                instructions: insts,
                switched: Arc::new(AtomicBool::new(false)),
                function: Arc::new(AtomicPtr::new(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                                               *mut fn(&Vec<GI>, &Vec<GI>)->GI>(dummy)}))
        };

        {
            let fo_c = result.clone();
            thread::spawn(move || {
                &fo_c.compile(debug, &suffix);
            })
        };
        result
    }

    fn compile(&self, debug: bool, suffix: &String) {
        let mut process = Command::new("build_func.sh");
        if debug {
            println!("compile suffix: {}", suffix);
        }
        let ignore = process.arg(suffix.clone()).output()
            .unwrap_or_else(|e| {panic!("Could not compile: {}", e)});

        if !ignore.status.success() {
            panic!("Could not compile: {}\n----\n{}", String::from_utf8(ignore.stdout).unwrap(),
                   String::from_utf8(ignore.stderr).unwrap());
        }

        DynamicLibrary::prepend_search_path(Path::new("./.compiled"));
        let dylib: String = "libfunc_".to_string() + suffix + ".so".into();

        let f = match DynamicLibrary::open(Some(Path::new(&dylib)))
        {
            Ok(lib) => lib,
            Err(err) => panic!("Could not load library: {}", err)
        };
        let g = unsafe{match f.symbol("gelpia_func") {
            Ok(func) => transmute::<*mut u32, fn(&Vec<GI>, &Vec<GI>)->GI>(func),
            Err(err) => panic!("Could not load function: {}", err),
        }};

        self.set(g, f);
    }
}

#[test]
fn test1() {
    let constants = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&constants,
                         &"c0,c1,o-,c2,o+".to_string());
    let expected = ((GI::new_c("1") - GI::new_c("2")) + GI::new_c("3")).to_string();
    assert!(f.call(&vec![]).to_string() == expected);
}

#[test]
fn test2() {
    let consts = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&consts,
                         &"c0,fsin,c1,o*,fcos,c2,o/".to_string());
    let expected = cos(sin(consts[0]) * consts[1])/consts[2];
    assert!(f.call(&vec![]).to_string() == expected.to_string());
}

#[test]
fn sum_inputs() {
    let consts = vec![];
    let v = vec![GI::new_d(1.0, 1.0), GI::new_d(1.0, 2.0), GI::new_d(1.0, 3.0),
                 GI::new_d(1.0, 4.0), GI::new_d(1.0, 5.0), GI::new_d(1.0, 6.0)];
    let f = FuncObj::new(&consts,
                         &"i0,i1, o+, i2, o+, i3, o+, i4, o+, i5, o+".to_string());
    let expected = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test3() {
    let consts = vec![GI::new_c("0.1")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fsin".to_string());
    let expected = sin(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test4() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fabs".to_string());
    let expected = abs(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test5() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fcos".to_string());
    let expected = cos(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test6() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, ftan".to_string());
    let expected = tan(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test7() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fexp".to_string());
    let expected = exp(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test8() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fneg".to_string());
    let expected = (-consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test9() {
    let consts = vec![GI::new_c("[0.1, .2]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, flog".to_string());
    let expected = log(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test10() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, c1, op".to_string());
    let expected = powi(consts[0], consts[1]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test_power() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, p-1, c1, p2, o+".to_string());
    let expected = (pow(consts[0], -1) + pow(consts[1], 2)).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
<commit_msg>Removed warning<commit_after>#![feature(asm)]

// External libraries
extern crate dylib;
use dylib::DynamicLibrary;

use std::path::Path;
use std::mem::transmute;

use std::process::Command;
use std::thread;

use std::sync::atomic::{AtomicBool, Ordering, AtomicPtr};
use std::option::Option;

use std::sync::{Arc, RwLock};
use std::fmt;

// Internal libraries
extern crate gr;
use gr::*;

#[derive(Clone)]
enum OpType {
    Func(String),
    Const(usize),
    Var(usize),
    UVar(usize),
    Op(String),
    Pow(i32)
}

impl fmt::Display for OpType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {

        let outstring = match self {
            &OpType::Func(ref s) => format!("f{}", s),
            &OpType::Const(i) => format!("c{}", i),
            &OpType::Var(i) => format!("i{}", i),
            &OpType::UVar(i) => format!("v{}", i),
            &OpType::Op(ref s) => format!("o{}", s),
            &OpType::Pow(i) => format!("p{}", i)
        };
        write!(f, "{}", outstring)
    }

}

#[derive(Clone)]
pub struct FuncObj {
    handle: Arc<RwLock<Option<DynamicLibrary>>>,
    user_vars: Vec<GI>,
    constants: Vec<GI>,
    instructions: Vec<OpType>,
    switched: Arc<AtomicBool>,
    function: Arc<AtomicPtr<fn(&Vec<GI>, &Vec<GI>) -> GI>>,

}

unsafe impl Sync for FuncObj {}
unsafe impl Send for FuncObj {}
fn dummy(_x: &Vec<GI>, _c: &Vec<GI>) -> GI {
    GI::new_c("1.0").unwrap()
}

impl FuncObj {
    pub fn call(&self, _x: &Vec<GI>) -> GI {
        if self.switched.load(Ordering::Acquire) {
            let real_func = unsafe{
                std::mem::transmute::<*mut fn(&Vec<GI>, &Vec<GI>)->GI,
                                      fn(&Vec<GI>, &Vec<GI>)->GI>(
                    (self.function.load(Ordering::Acquire)))};
            real_func(_x, &self.constants)
        }
        else {
            self.interpreted(_x, &self.constants)
        }
    }

    fn set(&self, f: fn(&Vec<GI>,&Vec<GI>) -> GI, handle: DynamicLibrary) {
        // This FuncObj owns the handle from the dynamic lib as the lifetime of
        // f is tied to the lifetime of the handle.
        self.function.store(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                         *mut fn(&Vec<GI>, &Vec<GI>)->GI>(f)}, Ordering::Release);
        let mut h = self.handle.write().unwrap();
        *h = Some(handle);
        self.switched.store(true, Ordering::Release);
    }

    fn interpreted(&self, _x: &Vec<GI>, _c: &Vec<GI>) -> GI {
        let mut stack: Vec<GI> = Vec::new();
        for inst in &self.instructions {
            match inst {
                &OpType::Func(ref s) => {
                    let op = stack.last_mut().unwrap();
                    match s.as_str() {
                        "abs" => op.abs(),
                        "sin" => op.sin(),
                        "cos" => op.cos(),
                        "tan" => op.tan(),
                        "exp" => op.exp(),
                        "log" => op.log(),
                        "neg" => op.neg(),
                        "sqrt" => op.sqrt(),
                        _     => unreachable!()
                    };
                },
                &OpType::Const(i) => {
                    stack.push(self.constants[i]);
                },
                &OpType::Var(i) => {
                    stack.push(_x[i]);
                },
                &OpType::UVar(i) => {
                    stack.push(self.user_vars[i]);
                },
                &OpType::Op(ref s) => {
                    let right = stack.pop().unwrap();
                    let left = stack.last_mut().unwrap();
                    match s.as_str() {
                        "+" => left.add(right),
                        "-" => left.sub(right),
                        "*" => left.mul(right),
                        "/" => left.div(right),
                        "p" => left.powi(right),
                        _   => unreachable!()
                    };
                },
                &OpType::Pow(exp) => {
                    let arg = stack.last_mut().unwrap();
                    arg.pow(exp);
                }
            }
        }
        stack[0]
    }

    pub fn new(consts: &Vec<GI>, instructions: &String, debug: bool, suffix: String) -> FuncObj {
        let mut insts = vec![];

        for inst in instructions.split(',') {
            let dummy = inst.trim().to_string();
            let (first, rest) = dummy.split_at(1);
            insts.push(match first {
                "c" => OpType::Const(rest.to_string().parse::<usize>().unwrap()),
                "i" => OpType::Var(rest.to_string().parse::<usize>().unwrap()),
                "v" => OpType::UVar(rest.to_string().parse::<usize>().unwrap()),
                "o" => OpType::Op(rest.to_string()),
                "f" => OpType::Func(rest.to_string()),
                "p" => OpType::Pow(rest.to_string().parse::<i32>().unwrap()),
                _   => panic!()
            });
        }
        let result =
            FuncObj{handle: Arc::new(RwLock::new(Option::None)),
                user_vars: vec![],
                constants: consts.clone(),
                instructions: insts,
                switched: Arc::new(AtomicBool::new(false)),
                function: Arc::new(AtomicPtr::new(unsafe{std::mem::transmute::<fn(&Vec<GI>, &Vec<GI>)->GI,
                                                                               *mut fn(&Vec<GI>, &Vec<GI>)->GI>(dummy)}))
        };

        {
            let fo_c = result.clone();
            thread::spawn(move || {
                &fo_c.compile(debug, &suffix);
            })
        };
        result
    }

    fn compile(&self, debug: bool, suffix: &String) {
        let mut process = Command::new("build_func.sh");
        if debug {
            println!("compile suffix: {}", suffix);
        }
        let ignore = process.arg(suffix.clone()).output()
            .unwrap_or_else(|e| {panic!("Could not compile: {}", e)});

        if !ignore.status.success() {
            panic!("Could not compile: {}\n----\n{}", String::from_utf8(ignore.stdout).unwrap(),
                   String::from_utf8(ignore.stderr).unwrap());
        }

        DynamicLibrary::prepend_search_path(Path::new("./.compiled"));
        let dylib: String = "libfunc_".to_string() + suffix + ".so".into();

        let f = match DynamicLibrary::open(Some(Path::new(&dylib)))
        {
            Ok(lib) => lib,
            Err(err) => panic!("Could not load library: {}", err)
        };
        let g = unsafe{match f.symbol("gelpia_func") {
            Ok(func) => transmute::<*mut u32, fn(&Vec<GI>, &Vec<GI>)->GI>(func),
            Err(err) => panic!("Could not load function: {}", err),
        }};

        self.set(g, f);
    }
}

#[test]
fn test1() {
    let constants = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&constants,
                         &"c0,c1,o-,c2,o+".to_string());
    let expected = ((GI::new_c("1") - GI::new_c("2")) + GI::new_c("3")).to_string();
    assert!(f.call(&vec![]).to_string() == expected);
}

#[test]
fn test2() {
    let consts = vec![GI::new_c("1"), GI::new_c("2"), GI::new_c("3")];
    let f = FuncObj::new(&consts,
                         &"c0,fsin,c1,o*,fcos,c2,o/".to_string());
    let expected = cos(sin(consts[0]) * consts[1])/consts[2];
    assert!(f.call(&vec![]).to_string() == expected.to_string());
}

#[test]
fn sum_inputs() {
    let consts = vec![];
    let v = vec![GI::new_d(1.0, 1.0), GI::new_d(1.0, 2.0), GI::new_d(1.0, 3.0),
                 GI::new_d(1.0, 4.0), GI::new_d(1.0, 5.0), GI::new_d(1.0, 6.0)];
    let f = FuncObj::new(&consts,
                         &"i0,i1, o+, i2, o+, i3, o+, i4, o+, i5, o+".to_string());
    let expected = (v[0] + v[1] + v[2] + v[3] + v[4] + v[5]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test3() {
    let consts = vec![GI::new_c("0.1")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fsin".to_string());
    let expected = sin(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test4() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fabs".to_string());
    let expected = abs(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test5() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fcos".to_string());
    let expected = cos(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test6() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, ftan".to_string());
    let expected = tan(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test7() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fexp".to_string());
    let expected = exp(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test8() {
    let consts = vec![GI::new_c("[-3, 4]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, fneg".to_string());
    let expected = (-consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}

#[test]
fn test9() {
    let consts = vec![GI::new_c("[0.1, .2]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                &"c0, flog".to_string());
    let expected = log(consts[0]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test10() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, c1, op".to_string());
    let expected = powi(consts[0], consts[1]).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
#[test]
fn test_power() {
    let consts = vec![GI::new_c("[2.0, 3.0]"),
                      GI::new_c("[1.5, 2.5]")];
    let v = vec![];
    let f = FuncObj::new(&consts,
                         &"c0, p-1, c1, p2, o+".to_string());
    let expected = (pow(consts[0], -1) + pow(consts[1], 2)).to_string();
    let real = f.call(&v).to_string();
    assert!(real == expected, "real = {}, expected = {}", real, expected);
}
<|endoftext|>