<commit_before>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::hir::def_id::DefId;
use rustc::lint::*;
use rustc::ty::{self, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
            multispan_sugg, paths};
use std::collections::{HashSet, HashMap};

/// **What it does:** Checks for functions taking arguments by value, but not consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId
    ) {
        if in_macro(cx, span) {
            return;
        }

        if !matches!(kind, FnKind::ItemFn(..)) {
            return;
        }

        // Allows these to be passed by value.
        let fn_trait = cx.tcx.lang_items.fn_trait().expect("failed to find `Fn` trait");
        let asref_trait = get_trait_def_id(cx, &paths::ASREF_TRAIT).expect("failed to find `AsRef` trait");
        let borrow_trait = get_trait_def_id(cx, &paths::BORROW_TRAIT).expect("failed to find `Borrow` trait");

        let preds: Vec<ty::Predicate> = {
            let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
            traits::elaborate_predicates(cx.tcx, parameter_env.caller_bounds.clone())
                .filter(|p| !p.is_global())
                .collect()
        };

        // Collect moved variables and spans which will need dereferencings from the function body.
        let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let infcx = cx.tcx.borrowck_fake_infer_ctxt(body.id());
            {
                let mut v = euv::ExprUseVisitor::new(&mut ctx, &infcx);
                v.consume_body(body);
            }
            ctx
        };

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);
        let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
        let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
        let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, fn_sig);

        for ((input, ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

            // Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
            // This is needed due to the `Borrow<T> for T` blanket impl.
            let implements_borrow_trait = preds.iter()
                .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                    Some(poly_trait_ref.skip_binder())
                } else {
                    None
                })
                .filter(|tpred| tpred.def_id() == borrow_trait && &tpred.self_ty() == ty)
                .any(|tpred| &tpred.input_types().nth(1).expect("Borrow trait must have an parameter") != ty);

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty, node_id),
                !implements_trait(cx, ty, fn_trait, &[], Some(node_id)),
                !implements_trait(cx, ty, asref_trait, &[], Some(node_id)),
                !implements_borrow_trait,

                let PatKind::Binding(mode, defid, ..) = arg.pat.node,
                !moved_vars.contains(&defid),
            ], {
                // Note: `toplevel_ref_arg` warns if `BindByRef`
                let m = match mode {
                    BindingMode::BindByRef(m) | BindingMode::BindByValue(m) => m,
                };
                if m == Mutability::MutMutable {
                    continue;
                }

                // Suggestion logic
                let sugg = |db: &mut DiagnosticBuilder| {
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| &*seg.name.as_str() == "Vec")
                            .map(|ps| ps.parameters.types()[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        &format!("consider changing the type to `{}`", slice_ty),
                                        slice_ty);
                        return; // `Vec` and `String` cannot be destructured - no need for `*` suggestion
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        db.span_suggestion(input.span,
                                           "consider changing the type to `&str`",
                                           "&str".to_string());
                        return;
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = spans_need_deref.get(&defid) {
                        spans.extend(deref_span.iter().cloned()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))));
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(cx,
                          NEEDLESS_PASS_BY_VALUE,
                          input.span,
                          "this argument is passed by value, but not consumed in the function body",
                          sugg);
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<DefId>,
    /// Spans which need to be prefixed with `*` for dereferencing the suggested additional
    /// reference.
    spans_need_deref: HashMap<DefId, HashSet<Span>>,
}

impl<'a, 'tcx: 'a> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        MovedVariablesCtxt {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
                self.moved_vars.insert(def_id);
        }}
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        }

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        }

                        _ => {}
                    }
                }
            }
        }}
    }
}

impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(
        &mut self,
        _: NodeId,
        _: Span,
        _: mc::cmt<'tcx>,
        _: &'tcx ty::Region,
        _: ty::BorrowKind,
        _: euv::LoanCause
    ) {
    }

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) |
            mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<commit_msg>Ensure deref_span is empty for Vec and String<commit_after>use rustc::hir::*;
use rustc::hir::intravisit::FnKind;
use rustc::hir::def_id::DefId;
use rustc::lint::*;
use rustc::ty::{self, TypeFoldable};
use rustc::traits;
use rustc::middle::expr_use_visitor as euv;
use rustc::middle::mem_categorization as mc;
use syntax::ast::NodeId;
use syntax_pos::Span;
use syntax::errors::DiagnosticBuilder;
use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
            multispan_sugg, paths};
use std::collections::{HashSet, HashMap};

/// **What it does:** Checks for functions taking arguments by value, but not consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// fn foo(v: Vec<i32>) {
///     assert_eq!(v.len(), 42);
/// }
/// ```
declare_lint! {
    pub NEEDLESS_PASS_BY_VALUE,
    Warn,
    "functions taking arguments by value, but not consuming them in its body"
}

pub struct NeedlessPassByValue;

impl LintPass for NeedlessPassByValue {
    fn get_lints(&self) -> LintArray {
        lint_array![NEEDLESS_PASS_BY_VALUE]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl,
        body: &'tcx Body,
        span: Span,
        node_id: NodeId
    ) {
        if in_macro(cx, span) {
            return;
        }

        if !matches!(kind, FnKind::ItemFn(..)) {
            return;
        }

        // Allows these to be passed by value.
        let fn_trait = cx.tcx.lang_items.fn_trait().expect("failed to find `Fn` trait");
        let asref_trait = get_trait_def_id(cx, &paths::ASREF_TRAIT).expect("failed to find `AsRef` trait");
        let borrow_trait = get_trait_def_id(cx, &paths::BORROW_TRAIT).expect("failed to find `Borrow` trait");

        let preds: Vec<ty::Predicate> = {
            let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
            traits::elaborate_predicates(cx.tcx, parameter_env.caller_bounds.clone())
                .filter(|p| !p.is_global())
                .collect()
        };

        // Collect moved variables and spans which will need dereferencings from the function body.
        let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = {
            let mut ctx = MovedVariablesCtxt::new(cx);
            let infcx = cx.tcx.borrowck_fake_infer_ctxt(body.id());
            {
                let mut v = euv::ExprUseVisitor::new(&mut ctx, &infcx);
                v.consume_body(body);
            }
            ctx
        };

        let fn_def_id = cx.tcx.hir.local_def_id(node_id);
        let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
        let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
        let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, fn_sig);

        for ((input, ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

            // Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
            // This is needed due to the `Borrow<T> for T` blanket impl.
            let implements_borrow_trait = preds.iter()
                .filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
                    Some(poly_trait_ref.skip_binder())
                } else {
                    None
                })
                .filter(|tpred| tpred.def_id() == borrow_trait && &tpred.self_ty() == ty)
                .any(|tpred| &tpred.input_types().nth(1).expect("Borrow trait must have an parameter") != ty);

            if_let_chain! {[
                !is_self(arg),
                !ty.is_mutable_pointer(),
                !is_copy(cx, ty, node_id),
                !implements_trait(cx, ty, fn_trait, &[], Some(node_id)),
                !implements_trait(cx, ty, asref_trait, &[], Some(node_id)),
                !implements_borrow_trait,

                let PatKind::Binding(mode, defid, ..) = arg.pat.node,
                !moved_vars.contains(&defid),
            ], {
                // Note: `toplevel_ref_arg` warns if `BindByRef`
                let m = match mode {
                    BindingMode::BindByRef(m) | BindingMode::BindByValue(m) => m,
                };
                if m == Mutability::MutMutable {
                    continue;
                }

                // Suggestion logic
                let sugg = |db: &mut DiagnosticBuilder| {
                    let deref_span = spans_need_deref.get(&defid);
                    if_let_chain! {[
                        match_type(cx, ty, &paths::VEC),
                        let TyPath(QPath::Resolved(_, ref path)) = input.node,
                        let Some(elem_ty) = path.segments.iter()
                            .find(|seg| &*seg.name.as_str() == "Vec")
                            .map(|ps| ps.parameters.types()[0]),
                    ], {
                        let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                        db.span_suggestion(input.span,
                                        &format!("consider changing the type to `{}`", slice_ty),
                                        slice_ty);
                        assert!(deref_span.is_none());
                        return; // `Vec` and `String` cannot be destructured - no need for `*` suggestion
                    }}

                    if match_type(cx, ty, &paths::STRING) {
                        db.span_suggestion(input.span,
                                           "consider changing the type to `&str`",
                                           "&str".to_string());
                        assert!(deref_span.is_none());
                        return;
                    }

                    let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                    // Suggests adding `*` to dereference the added reference.
                    if let Some(deref_span) = deref_span {
                        spans.extend(deref_span.iter().cloned()
                                     .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))));
                        spans.sort_by_key(|&(span, _)| span);
                    }
                    multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
                };

                span_lint_and_then(cx,
                          NEEDLESS_PASS_BY_VALUE,
                          input.span,
                          "this argument is passed by value, but not consumed in the function body",
                          sugg);
            }}
        }
    }
}

struct MovedVariablesCtxt<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    moved_vars: HashSet<DefId>,
    /// Spans which need to be prefixed with `*` for dereferencing the suggested additional
    /// reference.
    spans_need_deref: HashMap<DefId, HashSet<Span>>,
}

impl<'a, 'tcx: 'a> MovedVariablesCtxt<'a, 'tcx> {
    fn new(cx: &'a LateContext<'a, 'tcx>) -> Self {
        MovedVariablesCtxt {
            cx: cx,
            moved_vars: HashSet::new(),
            spans_need_deref: HashMap::new(),
        }
    }

    fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
                self.moved_vars.insert(def_id);
        }}
    }

    fn non_moving_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>) {
        let cmt = unwrap_downcast_or_interior(cmt);

        if_let_chain! {[
            let mc::Categorization::Local(vid) = cmt.cat,
            let Some(def_id) = self.cx.tcx.hir.opt_local_def_id(vid),
        ], {
            let mut id = matched_pat.id;
            loop {
                let parent = self.cx.tcx.hir.get_parent_node(id);
                if id == parent {
                    // no parent
                    return;
                }
                id = parent;

                if let Some(node) = self.cx.tcx.hir.find(id) {
                    match node {
                        map::Node::NodeExpr(e) => {
                            // `match` and `if let`
                            if let ExprMatch(ref c, ..) = e.node {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(c.span);
                            }
                        }

                        map::Node::NodeStmt(s) => {
                            // `let <pat> = x;`
                            if_let_chain! {[
                                let StmtDecl(ref decl, _) = s.node,
                                let DeclLocal(ref local) = decl.node,
                            ], {
                                self.spans_need_deref
                                    .entry(def_id)
                                    .or_insert_with(HashSet::new)
                                    .insert(local.init
                                        .as_ref()
                                        .map(|e| e.span)
                                        .expect("`let` stmt without init aren't caught by match_pat"));
                            }}
                        }

                        _ => {}
                    }
                }
            }
        }}
    }
}

impl<'a, 'tcx: 'a> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
    fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_id, consume_span, cmt);
        }
    }

    fn matched_pat(&mut self, matched_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::MatchMode) {
        if let euv::MatchMode::MovingMatch = mode {
            self.move_common(matched_pat.id, matched_pat.span, cmt);
        } else {
            self.non_moving_pat(matched_pat, cmt);
        }
    }

    fn consume_pat(&mut self, consume_pat: &Pat, cmt: mc::cmt<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move(_) = mode {
            self.move_common(consume_pat.id, consume_pat.span, cmt);
        }
    }

    fn borrow(
        &mut self,
        _: NodeId,
        _: Span,
        _: mc::cmt<'tcx>,
        _: &'tcx ty::Region,
        _: ty::BorrowKind,
        _: euv::LoanCause
    ) {
    }

    fn mutate(&mut self, _: NodeId, _: Span, _: mc::cmt<'tcx>, _: euv::MutateMode) {}

    fn decl_without_init(&mut self, _: NodeId, _: Span) {}
}

fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
    loop {
        match cmt.cat.clone() {
            mc::Categorization::Downcast(c, _) |
            mc::Categorization::Interior(c, _) => {
                cmt = c;
            },
            _ => return cmt,
        }
    }
}
<|endoftext|>


<commit_before>//! `GET /_matrix/client/*/rooms/{roomId}/aliases`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidaliases

    use ruma_api::ruma_api;
    use ruma_identifiers::{RoomAliasId, RoomId};

    ruma_api! {
        metadata: {
            description: "Get a list of aliases maintained by the local server for the given room.",
            method: GET,
            name: "aliases",
            r0_path: "/_matrix/client/r0/rooms/:room_id/aliases",
            stable_path: "/_matrix/client/v3/rooms/:room_id/aliases",
            rate_limited: true,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room ID to get aliases of.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,
        }

        response: {
            /// The server's local aliases on the room.
            pub aliases: Vec<Box<RoomAliasId>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room ID.
        pub fn new(room_id: &'a RoomId) -> Self {
            Self { room_id }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given aliases.
        pub fn new(aliases: Vec<Box<RoomAliasId>>) -> Self {
            Self { aliases }
        }
    }
}
<commit_msg>ruma-client-api: Add unstable path for MSC2432<commit_after>//! `GET /_matrix/client/*/rooms/{roomId}/aliases`

pub mod v3 {
    //! `/v3/` ([spec])
    //!
    //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidaliases

    use ruma_api::ruma_api;
    use ruma_identifiers::{RoomAliasId, RoomId};

    ruma_api! {
        metadata: {
            description: "Get a list of aliases maintained by the local server for the given room.",
            method: GET,
            name: "aliases",
            r0_path: "/_matrix/client/r0/rooms/:room_id/aliases",
            stable_path: "/_matrix/client/v3/rooms/:room_id/aliases",
            unstable_path: "/_matrix/client/unstable/org.matrix.msc2432/rooms/:room_id/aliases",
            rate_limited: true,
            authentication: AccessToken,
            added: 1.0,
        }

        request: {
            /// The room ID to get aliases of.
            #[ruma_api(path)]
            pub room_id: &'a RoomId,
        }

        response: {
            /// The server's local aliases on the room.
            pub aliases: Vec<Box<RoomAliasId>>,
        }

        error: crate::Error
    }

    impl<'a> Request<'a> {
        /// Creates a new `Request` with the given room ID.
        pub fn new(room_id: &'a RoomId) -> Self {
            Self { room_id }
        }
    }

    impl Response {
        /// Creates a new `Response` with the given aliases.
        pub fn new(aliases: Vec<Box<RoomAliasId>>) -> Self {
            Self { aliases }
        }
    }
}
<|endoftext|>


<commit_before>extern crate curl;
extern crate crypto_hash;

#[cfg(feature = "pi")]
extern crate wiringpi;

use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::{thread, time};
use std::str;

use curl::easy::Easy;

use crypto_hash::{Algorithm, hex_digest};

static CONFIG_FILE_NAME: &'static str = "doorstate.config";

struct API {
    url: String,
    rate_ms: i64,
    pre_shared_key: String
}

struct GPIO {
    pin: i8
}

fn config() -> (API, GPIO)
{
    let path = Path::new(CONFIG_FILE_NAME);
    let path_display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!(
            "Couldn't open {}: {}",
            path_display,
            why.description()),
        Ok(file) => file,
    };

    let mut config = String::new();

    match file.read_to_string(&mut config) {
        Err(why) => panic!(
            "Couldn't read {}: {}",
            path_display,
            why.description()),
        //Ok(_) => print!("{} contains:\n{}", path_display, config),
        Ok(_) => {},
    };

    let mut api = API {
        url: String::new(),
        pre_shared_key: String::new(),
        rate_ms: -1
    };

    let mut gpio = GPIO {
        pin: -1
    };

    for line in config.lines() {
        // Ignore comments (lines starting with '#')
        if !line.trim().starts_with("#") {
            //println!("{}", line);
            let tokens: Vec<&str> = line.splitn(2, "=").collect();

            let key = tokens[0].trim();
            let value = tokens[1].trim();

            match key {
                "api.url" => api.url = value.to_string(),
                "api.rate-ms" => api.rate_ms = value.parse::<i64>().unwrap(),
                "api.pre-shared-key" => api.pre_shared_key = value.to_string(),
                "gpio.pin" => gpio.pin = value.parse::<i8>().unwrap(),
                _ => {},
            }
        }
    }

    (api, gpio)
}

#[cfg(feature = "pi")]
fn read_doorstate(pin: InputPin) -> String {
    let mut status: String  = "geschlossen";

    if pin.digital_read() == wiringpi::pin::Value::Low {
        status = "offen";
    }

    return status
}

#[allow(unused_variables)]
fn read_doorstate(pin: ()) -> String {
    return "offen".to_string();
}

fn send_doorstate(baseurl: &String, pre_shared_key: &String, status: &String) {
    let mut easy = Easy::new();
    let mut dst = Vec::new();

    easy.url(&baseurl).unwrap();

    // This scope is required to end the borrow of dst
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            dst.extend_from_slice(data);
            Ok(data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let challenge = str::from_utf8(&dst).unwrap();
    let mut response = dst.to_vec();

    response.extend_from_slice(pre_shared_key.as_bytes());

    let response_hashed = hex_digest(Algorithm::SHA256, response);
    let mut url: String = String::new();

    url.push_str(baseurl);
    url.push_str("?");
    url.push_str("challenge=");
    url.push_str(challenge);
    url.push_str("&");
    url.push_str("response=");
    url.push_str(&response_hashed);
    url.push_str("&");
    url.push_str("status=");
    url.push_str(status);

    easy.url(&url).unwrap();

    let transfer = easy.transfer();

    transfer.perform().unwrap();
}

#[cfg(feature = "pi")]
fn setup_gpio(pin: i8) -> InputPin {
    let pi = wiringpi::setup();
    let pin = pi.input_pin(pin);

    return pin
}

#[allow(unused_variables)]
fn setup_gpio(pin: i8) {
}

fn main() {
    let (api, gpio) = config();
    let duration;
    let mut status_last: String = "".to_string();

    let pin = setup_gpio(gpio.pin);

    if api.rate_ms > -1 {
        duration = time::Duration::from_millis(api.rate_ms as u64);
    } else {
        duration = time::Duration::from_millis(0);
    }

    loop {
        let status_new = read_doorstate(pin);

        if status_new != status_last {
            send_doorstate(&api.url, &api.pre_shared_key, &status_new);

            status_last = status_new;
        }

        if duration > time::Duration::from_millis(0) {
            thread::sleep(duration);
        }
    }
}
<commit_msg>Fixes for pi feature on Raspberry Pi<commit_after>extern crate curl;
extern crate crypto_hash;

#[cfg(feature = "pi")]
extern crate wiringpi;

use std::path::Path;
use std::fs::File;
use std::error::Error;
use std::io::prelude::*;
use std::{thread, time};
use std::str;

use curl::easy::Easy;

use crypto_hash::{Algorithm, hex_digest};

#[cfg(feature = "pi")]
use wiringpi::pin::{WiringPi, InputPin};

static CONFIG_FILE_NAME: &'static str = "doorstate.config";

struct API {
    url: String,
    rate_ms: i64,
    pre_shared_key: String
}

struct GPIO {
    pin: i8
}

fn config() -> (API, GPIO)
{
    let path = Path::new(CONFIG_FILE_NAME);
    let path_display = path.display();

    let mut file = match File::open(&path) {
        Err(why) => panic!(
            "Couldn't open {}: {}",
            path_display,
            why.description()),
        Ok(file) => file,
    };

    let mut config = String::new();

    match file.read_to_string(&mut config) {
        Err(why) => panic!(
            "Couldn't read {}: {}",
            path_display,
            why.description()),
        //Ok(_) => print!("{} contains:\n{}", path_display, config),
        Ok(_) => {},
    };

    let mut api = API {
        url: String::new(),
        pre_shared_key: String::new(),
        rate_ms: -1
    };

    let mut gpio = GPIO {
        pin: -1
    };

    for line in config.lines() {
        // Ignore comments (lines starting with '#')
        if !line.trim().starts_with("#") {
            //println!("{}", line);
            let tokens: Vec<&str> = line.splitn(2, "=").collect();

            let key = tokens[0].trim();
            let value = tokens[1].trim();

            match key {
                "api.url" => api.url = value.to_string(),
                "api.rate-ms" => api.rate_ms = value.parse::<i64>().unwrap(),
                "api.pre-shared-key" => api.pre_shared_key = value.to_string(),
                "gpio.pin" => gpio.pin = value.parse::<i8>().unwrap(),
                _ => {},
            }
        }
    }

    (api, gpio)
}

#[cfg(feature = "pi")]
fn read_doorstate(pin: &InputPin<WiringPi>) -> String {
    let mut status: String  = "geschlossen".to_string();

    if pin.digital_read() == wiringpi::pin::Value::Low {
        status = "offen".to_string();
    }

    return status;
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn read_doorstate(pin: &()) -> String {
    return "offen".to_string();
}

fn send_doorstate(baseurl: &String, pre_shared_key: &String, status: &String) {
    let mut easy = Easy::new();
    let mut dst = Vec::new();

    easy.url(&baseurl).unwrap();

    // This scope is required to end the borrow of dst
    {
        let mut transfer = easy.transfer();
        transfer.write_function(|data| {
            dst.extend_from_slice(data);
            Ok(data.len())
        }).unwrap();
        transfer.perform().unwrap();
    }

    let challenge = str::from_utf8(&dst).unwrap();
    let mut response = dst.to_vec();

    response.extend_from_slice(pre_shared_key.as_bytes());

    let response_hashed = hex_digest(Algorithm::SHA256, response);
    let mut url: String = String::new();

    url.push_str(baseurl);
    url.push_str("?");
    url.push_str("challenge=");
    url.push_str(challenge);
    url.push_str("&");
    url.push_str("response=");
    url.push_str(&response_hashed);
    url.push_str("&");
    url.push_str("status=");
    url.push_str(status);

    easy.url(&url).unwrap();

    let transfer = easy.transfer();

    transfer.perform().unwrap();
}

#[cfg(feature = "pi")]
fn setup_gpio(pin_no: i8) -> InputPin<WiringPi> {
    let pi = wiringpi::setup();
    let pin = pi.input_pin(pin_no as u16);

    pin
}

#[cfg(not(feature = "pi"))]
#[allow(unused_variables)]
fn setup_gpio(pin: i8) {
}

fn main() {
    let (api, gpio) = config();
    let duration;
    let mut status_last: String = "".to_string();

    let pin = setup_gpio(gpio.pin);

    if api.rate_ms > -1 {
        duration = time::Duration::from_millis(api.rate_ms as u64);
    } else {
        duration = time::Duration::from_millis(0);
    }

    loop {
        let status_new = read_doorstate(&pin);

        if status_new != status_last {
            send_doorstate(&api.url, &api.pre_shared_key, &status_new);

            status_last = status_new;
        }

        if duration > time::Duration::from_millis(0) {
            thread::sleep(duration);
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

extern crate device_tree_source;
extern crate mktemp;

use std::process::Command;
use std::path::{PathBuf, Path};

use mktemp::Temp;

use device_tree_source::parser::{ParseResult, parse_dt};
use device_tree_source::include::{IncludeError, include_files};

macro_rules! generate_tests {
    ($($name:ident $root:expr, $file:expr,)*) => {
        $(
            #[test]
            #[ignore]
            fn $name () {
                $crate::try_parse($root, $file)
            }
        )*
    }
}

include!("dts_file_names");

#[allow(dead_code)]
fn try_parse<P: AsRef<Path>>(root_dir: P, file: P) {
    let root_dir = root_dir.as_ref();
    let file = file.as_ref();
    println!("{}", file.display());

    ::std::env::set_current_dir(&root_dir)
              .expect("\nCould not change current directory. Does dts folder exist?\n");

    let mut cpp_temp_out = Temp::new_file().expect("Could not create temp file");
    let mut include_dirs = Vec::new();

    let mut cpp_command = Command::new("gcc");
    cpp_command.args(&["-E", "-nostdinc"])
               .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
               .args(&["-o", cpp_temp_out.as_ref().to_str().unwrap()])
               .arg(file.to_str().unwrap())
               .args(&["-I", "."]);
    include_dirs.push(PathBuf::from("."));

    if Path::new("include").is_dir() {
        cpp_command.args(&["-I", "include/"]);
        include_dirs.push(PathBuf::from("include/"));
    }

    if let Some(parent) = file.parent() {
        cpp_command.args(&["-I", &parent.to_string_lossy()]);
        include_dirs.push(parent.to_owned());

        let include = parent.join("include");
        if include.is_dir() {
            cpp_command.args(&["-I", &include.to_string_lossy()]);
            include_dirs.push(include);
        }
    } else {
        panic!("Could not get parent directory of file");
    }

    let include_output = cpp_command.output().expect("Failed to start CPP");

    if !include_output.status.success() {
        // Done to prevent a panic as the file will not have been written to
        cpp_temp_out.release();
        panic!("Failed to execute CPP.\n{:?}\n{}",
               cpp_command,
               String::from_utf8_lossy(&include_output.stderr));
    }

    let (buffer, bounds) = match include_files(&cpp_temp_out, &include_dirs) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err, path) => {
                    if let Some(path) = path {
                        panic!("IO error: {} {}", err, path.display());
                    } else {
                        panic!("IO error: {}", err);
                    }
                }
                IncludeError::LinemarkerInDtsi(path) =>
                    panic!("Extraneous linemarker found in DT include: {}",
                           path.to_string_lossy()),
                IncludeError::ParseError(_) =>
                    panic!("Failed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) =>
                    panic!("No bounds returned after parsing file: {}", path.to_string_lossy()),
            }
        }
    };

    // println!("{:#?}", bounds);

    match parse_dt(&buffer) {
        Ok(ParseResult::Complete(..)) => {},
        Ok(ParseResult::RemainingInput(dt_info, amends, rem)) => {
            // println!("DT Info:\n{:#?}", dt_info);
            // println!("Amends:\n{:#?}", amends);
            panic!("Input remaining after parsing:\n{}", String::from_utf8_lossy(rem));
        }
        Err(err) => {
            // println!("{:#?}", bounds);
            // println!("{}", String::from_utf8_lossy(&buffer));
            panic!("{:?}", err);
        }
    }
}
<commit_msg>iter_dts_files: Remove need to change working directory<commit_after>#![allow(unused_variables)]

extern crate device_tree_source;
extern crate mktemp;

use std::process::Command;
use std::path::Path;

use mktemp::Temp;

use device_tree_source::parser::{ParseResult, parse_dt};
use device_tree_source::include::{IncludeError, include_files};

macro_rules! generate_tests {
    ($($name:ident $root:expr, $file:expr,)*) => {
        $(
            #[test]
            #[ignore]
            fn $name () {
                $crate::try_parse($root, $file)
            }
        )*
    }
}

include!("dts_file_names");

#[allow(dead_code)]
fn try_parse<P: AsRef<Path>>(root_dir: P, file: P) {
    let root_dir = root_dir.as_ref();
    let file = file.as_ref();
    println!("{}", file.display());

    let mut cpp_temp_out = Temp::new_file().expect("Could not create temp file");
    let mut include_dirs = Vec::new();

    let mut cpp_command = Command::new("gcc");
    cpp_command.args(&["-E", "-nostdinc"])
               .args(&["-undef", "-D__DTS__", "-x", "assembler-with-cpp"])
               .args(&["-o", cpp_temp_out.as_ref().to_str().unwrap()])
               .arg(file.to_str().unwrap())
               .args(&["-I", &root_dir.to_string_lossy()]);
    include_dirs.push(root_dir.to_owned());

    if Path::new("include").is_dir() {
        let d = root_dir.join("include/");
        cpp_command.args(&["-I", &d.to_string_lossy()]);
        include_dirs.push(d);
    }

    if let Some(parent) = file.parent() {
        cpp_command.args(&["-I", &parent.to_string_lossy()]);
        include_dirs.push(parent.to_owned());

        let include = parent.join("include");
        if include.is_dir() {
            cpp_command.args(&["-I", &include.to_string_lossy()]);
            include_dirs.push(include);
        }
    } else {
        panic!("Could not get parent directory of file");
    }

    let include_output = cpp_command.output().expect("Failed to start CPP");

    if !include_output.status.success() {
        // Done to prevent a panic as the file will not have been written to
        cpp_temp_out.release();
        panic!("Failed to execute CPP.\n{:?}\n{}",
               cpp_command,
               String::from_utf8_lossy(&include_output.stderr));
    }

    let (buffer, bounds) = match include_files(&cpp_temp_out, &include_dirs) {
        Ok(x) => x,
        Err(e) => {
            match e {
                IncludeError::IOError(err, path) => {
                    if let Some(path) = path {
                        panic!("IO error: {} {}", err, path.display());
                    } else {
                        panic!("IO error: {}", err);
                    }
                }
                IncludeError::LinemarkerInDtsi(path) =>
                    panic!("Extraneous linemarker found in DT include: {}",
                           path.to_string_lossy()),
                IncludeError::ParseError(_) =>
                    panic!("Failed to convert line to byte offset for bounds tracking."),
                IncludeError::NoBoundReturned(path) =>
                    panic!("No bounds returned after parsing file: {}", path.to_string_lossy()),
            }
        }
    };

    // println!("{:#?}", bounds);

    match parse_dt(&buffer) {
        Ok(ParseResult::Complete(..)) => {},
        Ok(ParseResult::RemainingInput(dt_info, amends, rem)) => {
            // println!("DT Info:\n{:#?}", dt_info);
            // println!("Amends:\n{:#?}", amends);
            panic!("Input remaining after parsing:\n{}", String::from_utf8_lossy(rem));
        }
        Err(err) => {
            // println!("{:#?}", bounds);
            // println!("{}", String::from_utf8_lossy(&buffer));
            panic!("{:?}", err);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use libra_config::config::{NodeConfig, RoleType};
use libra_swarm::{client, swarm::LibraSwarm};
use libra_temppath::TempPath;
use std::path::Path;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(about = "Libra swarm to start local nodes")]
struct Args {
    /// Number of nodes to start (1 by default)
    #[structopt(short = "n", long, default_value = "1")]
    pub num_nodes: usize,
    /// Enable logging, by default spawned nodes will not perform logging
    #[structopt(short = "l", long)]
    pub enable_logging: bool,
    /// Start client
    #[structopt(short = "s", long)]
    pub start_client: bool,
    /// Directory used by launch_swarm to output LibraNodes' config files, logs, libradb, etc,
    /// such that user can still inspect them after exit.
    /// If unspecified, a temporary dir will be used and auto deleted.
    #[structopt(short = "c", long)]
    pub config_dir: Option<String>,
    /// If greater than 0, starts a full node swarm connected to the first node in the validator
    /// swarm.
    #[structopt(short = "f", long, default_value = "0")]
    pub num_full_nodes: usize,
}

fn main() {
    let args = Args::from_args();
    let num_nodes = args.num_nodes;
    let num_full_nodes = args.num_full_nodes;

    libra_logger::init_for_e2e_testing();

    let mut validator_swarm = LibraSwarm::configure_swarm(
        num_nodes,
        RoleType::Validator,
        args.config_dir.clone(),
        None, /* template config */
        None, /* upstream_config_dir */
    )
    .expect("Failed to configure validator swarm");

    let mut full_node_swarm = if num_full_nodes > 0 {
        Some(
            LibraSwarm::configure_swarm(
                num_full_nodes,
                RoleType::FullNode,
                None, /* config dir */
                None, /* template config */
                Some(String::from(
                    validator_swarm
                        .dir
                        .as_ref()
                        .join("0")
                        .to_str()
                        .expect("Failed to convert std::fs::Path to String"),
                )),
            )
            .expect("Failed to configure full node swarm"),
        )
    } else {
        None
    };
    validator_swarm
        .launch_attempt(RoleType::Validator, !args.enable_logging)
        .expect("Failed to launch validator swarm");
    if let Some(ref mut swarm) = full_node_swarm {
        swarm
            .launch_attempt(RoleType::FullNode, !args.enable_logging)
            .expect("Failed to launch full node swarm");
    }

    let faucet_key_file_path = &validator_swarm.config.faucet_key_path;
    let validator_config = NodeConfig::load(&validator_swarm.config.config_files[0]).unwrap();
    println!("To run the Libra CLI client in a separate process and connect to the validator nodes you just spawned, use this command:");
    println!(
        "\tcargo run --bin cli -- -a localhost -p {} -m {:?}",
        validator_config.admission_control.address.port(),
        faucet_key_file_path,
    );
    let node_address_list = validator_swarm
        .config
        .config_files
        .iter()
        .map(|config| {
            let port = NodeConfig::load(config)
                .unwrap()
                .admission_control
                .address
                .port();
            format!("localhost:{}", port)
        })
        .collect::<Vec<String>>()
        .join(",");
    println!("To run transaction generator run:");
    println!(
        "\tcargo run -p cluster-test -- --mint-file {:?} --swarm --peers {:?}  --emit-tx",
        faucet_key_file_path, node_address_list,
    );
    if let Some(ref swarm) = full_node_swarm {
        let full_node_config = NodeConfig::load(&swarm.config.config_files[0]).unwrap();
        println!("To connect to the full nodes you just spawned, use this command:");
        println!(
            "\tcargo run --bin cli -- -a localhost -p {} -m {:?}",
            full_node_config.admission_control.address.port(),
            faucet_key_file_path,
        );
    }

    let tmp_mnemonic_file = TempPath::new();
    tmp_mnemonic_file.create_as_file().unwrap();
    if args.start_client {
        let client = client::InteractiveClient::new_with_inherit_io(
            validator_swarm.get_ac_port(0),
            Path::new(&faucet_key_file_path),
            &tmp_mnemonic_file.path(),
        );
        println!("Loading client...");
        let _output = client.output().expect("Failed to wait on child");
        println!("Exit client.");
    } else {
        // Explicitly capture CTRL-C to drop LibraSwarm.
        let (tx, rx) = std::sync::mpsc::channel();
        ctrlc::set_handler(move || {
            tx.send(())
                .expect("failed to send unit when handling CTRL-C");
        })
        .expect("failed to set CTRL-C handler");
        println!("CTRL-C to exit.");
        rx.recv()
            .expect("failed to receive unit when handling CTRL-C");
    }
    if let Some(dir) = &args.config_dir {
        println!("Please manually cleanup {:?} after inspection", dir);
    }
    println!("Exit libra-swarm.");
}
<commit_msg>[libra_swarm] Enable module publishing by default in local libra swarm build.<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use libra_config::config::{NodeConfig, RoleType, VMPublishingOption};
use libra_swarm::{client, swarm::LibraSwarm};
use libra_temppath::TempPath;
use std::path::Path;
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
#[structopt(about = "Libra swarm to start local nodes")]
struct Args {
    /// Number of nodes to start (1 by default)
    #[structopt(short = "n", long, default_value = "1")]
    pub num_nodes: usize,
    /// Enable logging, by default spawned nodes will not perform logging
    #[structopt(short = "l", long)]
    pub enable_logging: bool,
    /// Start client
    #[structopt(short = "s", long)]
    pub start_client: bool,
    /// Directory used by launch_swarm to output LibraNodes' config files, logs, libradb, etc,
    /// such that user can still inspect them after exit.
    /// If unspecified, a temporary dir will be used and auto deleted.
    #[structopt(short = "c", long)]
    pub config_dir: Option<String>,
    /// If greater than 0, starts a full node swarm connected to the first node in the validator
    /// swarm.
    #[structopt(short = "f", long, default_value = "0")]
    pub num_full_nodes: usize,
}

fn main() {
    let args = Args::from_args();
    let num_nodes = args.num_nodes;
    let num_full_nodes = args.num_full_nodes;
    let mut dev_config = NodeConfig::default();
    dev_config.vm_config.publishing_options = VMPublishingOption::Open;

    libra_logger::init_for_e2e_testing();

    let mut validator_swarm = LibraSwarm::configure_swarm(
        num_nodes,
        RoleType::Validator,
        args.config_dir.clone(),
        Some(dev_config.clone()), /* template config */
        None,                     /* upstream_config_dir */
    )
    .expect("Failed to configure validator swarm");

    let mut full_node_swarm = if num_full_nodes > 0 {
        Some(
            LibraSwarm::configure_swarm(
                num_full_nodes,
                RoleType::FullNode,
                None,             /* config dir */
                Some(dev_config), /* template config */
                Some(String::from(
                    validator_swarm
                        .dir
                        .as_ref()
                        .join("0")
                        .to_str()
                        .expect("Failed to convert std::fs::Path to String"),
                )),
            )
            .expect("Failed to configure full node swarm"),
        )
    } else {
        None
    };
    validator_swarm
        .launch_attempt(RoleType::Validator, !args.enable_logging)
        .expect("Failed to launch validator swarm");
    if let Some(ref mut swarm) = full_node_swarm {
        swarm
            .launch_attempt(RoleType::FullNode, !args.enable_logging)
            .expect("Failed to launch full node swarm");
    }

    let faucet_key_file_path = &validator_swarm.config.faucet_key_path;
    let validator_config = NodeConfig::load(&validator_swarm.config.config_files[0]).unwrap();
    println!("To run the Libra CLI client in a separate process and connect to the validator nodes you just spawned, use this command:");
    println!(
        "\tcargo run --bin cli -- -a localhost -p {} -m {:?}",
        validator_config.admission_control.address.port(),
        faucet_key_file_path,
    );
    let node_address_list = validator_swarm
        .config
        .config_files
        .iter()
        .map(|config| {
            let port = NodeConfig::load(config)
                .unwrap()
                .admission_control
                .address
                .port();
            format!("localhost:{}", port)
        })
        .collect::<Vec<String>>()
        .join(",");
    println!("To run transaction generator run:");
    println!(
        "\tcargo run -p cluster-test -- --mint-file {:?} --swarm --peers {:?}  --emit-tx",
        faucet_key_file_path, node_address_list,
    );
    if let Some(ref swarm) = full_node_swarm {
        let full_node_config = NodeConfig::load(&swarm.config.config_files[0]).unwrap();
        println!("To connect to the full nodes you just spawned, use this command:");
        println!(
            "\tcargo run --bin cli -- -a localhost -p {} -m {:?}",
            full_node_config.admission_control.address.port(),
            faucet_key_file_path,
        );
    }

    let tmp_mnemonic_file = TempPath::new();
    tmp_mnemonic_file.create_as_file().unwrap();
    if args.start_client {
        let client = client::InteractiveClient::new_with_inherit_io(
            validator_swarm.get_ac_port(0),
            Path::new(&faucet_key_file_path),
            &tmp_mnemonic_file.path(),
        );
        println!("Loading client...");
        let _output = client.output().expect("Failed to wait on child");
        println!("Exit client.");
    } else {
        // Explicitly capture CTRL-C to drop LibraSwarm.
        let (tx, rx) = std::sync::mpsc::channel();
        ctrlc::set_handler(move || {
            tx.send(())
                .expect("failed to send unit when handling CTRL-C");
        })
        .expect("failed to set CTRL-C handler");
        println!("CTRL-C to exit.");
        rx.recv()
            .expect("failed to receive unit when handling CTRL-C");
    }
    if let Some(dir) = &args.config_dir {
        println!("Please manually cleanup {:?} after inspection", dir);
    }
    println!("Exit libra-swarm.");
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", "addr").with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", "admin").with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", "stop").with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_multi_match(vec![main_match_type.to_string()],
                                                         q.to_string())
        .with_boost(100)
        .build();
    let boost_zipcode_match_query = rs_q::build_multi_match(vec!["zip_codes.prefix".to_string()],
                                                            q.to_string())
        .with_boost(500)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    use rs_es::query::{CombinationMinimumShouldMatch, MinimumShouldMatch};
    let min_match = |min, threshold| {
        CombinationMinimumShouldMatch::new(MinimumShouldMatch::from(min),
                                           MinimumShouldMatch::from(threshold))
    };

    let minimum_should_match = match match_type {
        MatchType::Prefix => {
            // when we search [0, 2] fields, we need to match 100% of them
            // when we search [3, 4] fields, we need to match 90% of them
            // when we search [5, +] fields, we only need to match 70% on them
            // this way if the input has more fields than the documents
            // (additional information like the country, the region, ...)
            // they can be ignored
            MinimumShouldMatch::from(vec![min_match(2, 90f64), min_match(4, 70f64)])
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => MinimumShouldMatch::from(50f64),
    };

    let mut must = vec![rs_q::build_multi_match(vec![main_match_type.to_string(),
                                                     "zip_codes.prefix".to_string()],
                                                q.to_string())
                            .with_minimum_should_match(minimum_should_match)
                            .build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn is_existing_index(client: &mut rs_es::Client, index: &String) -> bool {
    match client.open_index(index) {
        Ok(_) => true,
        Err(_) => {
            debug!("{} index not found", index);
            false
        }
    }
}

fn make_indexes<'a>(all_data: &Option<bool>,
                    pt_dataset_index: &'a String,
                    client: &mut rs_es::Client)
                    -> Vec<&'a str> {
    if *all_data == Some(true) {
        return vec!["munin"];
    }

    let mut result: Vec<&str> = vec![];

    if !pt_dataset_index.is_empty() {
        if is_existing_index(client, &pt_dataset_index) {
            result.push(pt_dataset_index);
        }
    }
    if is_existing_index(client, &"munin_geo_data".to_string()) {
        result.push("munin_geo_data");
    }
    return result;
}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: &Option<bool>,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d))
        .unwrap_or("".to_string());
    let indexes = make_indexes(&all_data, &pt_dataset_index, &mut client);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes)
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: &Option<bool>,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             &all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape)));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              &all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape))
    } else {
        Ok(results)
    }
}
<commit_msg>is_existing_index function deleted<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use super::model;
use regex;
use rs_es;
use rs_es::query::Query as rs_q;
use rs_es::operations::search::SearchResult;
use rs_es::units as rs_u;
use mimir;
use serde_json;
use serde;

fn build_rs_client(cnx: &String) -> rs_es::Client {
    let re = regex::Regex::new(r"(?:https?://)?(?P<host>.+?):(?P<port>\d+)").unwrap();
    let cap = re.captures(&cnx).unwrap();
    let host = cap.name("host").unwrap().as_str();
    let port = cap.name("port").unwrap().as_str().parse::<u32>().unwrap();

    rs_es::Client::new(&host, port)
}

/// takes a ES json blob and build a Place from it
/// it uses the _type field of ES to know which type of the Place enum to fill
pub fn make_place(doc_type: String, value: Option<Box<serde_json::Value>>) -> Option<mimir::Place> {
    value.and_then(|v| {
        fn convert<T: serde::Deserialize>(v: serde_json::Value,
                                          f: fn(T) -> mimir::Place)
                                          -> Option<mimir::Place> {
            serde_json::from_value::<T>(v)
                .map_err(|err| warn!("Impossible to load ES result: {}", err))
                .ok()
                .map(f)
        }
        match doc_type.as_ref() {
            "addr" => convert(*v, mimir::Place::Addr),
            "street" => convert(*v, mimir::Place::Street),
            "admin" => convert(*v, mimir::Place::Admin),
            "poi" => convert(*v, mimir::Place::Poi),
            "stop" => convert(*v, mimir::Place::Stop),
            _ => {
                warn!("unknown ES return value, _type field = {}", doc_type);
                None
            }
        }
    })
}

#[derive(Debug, Eq, PartialEq)]
enum MatchType {
    Prefix,
    Fuzzy,
}

fn build_query(q: &str,
               match_type: MatchType,
               coord: &Option<model::Coord>,
               shape: Option<Vec<rs_es::units::Location>>)
               -> rs_es::query::Query {
    use rs_es::query::functions::Function;
    // we order the type of object we want
    // Note: the addresses are boosted more because even if we don't want them first
    // because they are more severely filtered
    let boost_addr = rs_q::build_term("_type", "addr").with_boost(5000).build();
    let boost_admin = rs_q::build_term("_type", "admin").with_boost(3000).build();
    let boost_stop = rs_q::build_term("_type", "stop").with_boost(2000).build();

    let main_match_type = match match_type {
        MatchType::Prefix => "label.prefix",
        MatchType::Fuzzy => "label.ngram",
    };

    let boost_main_match_query = rs_q::build_multi_match(vec![main_match_type.to_string()],
                                                         q.to_string())
        .with_boost(100)
        .build();
    let boost_zipcode_match_query = rs_q::build_multi_match(vec!["zip_codes.prefix".to_string()],
                                                            q.to_string())
        .with_boost(500)
        .build();

    let mut should_query = vec![boost_addr,
                                boost_admin,
                                boost_stop,
                                boost_main_match_query,
                                boost_zipcode_match_query];

    // for fuzzy search we also search by the prefix index (with a greater boost than ngram)
    // to have better results
    if match_type == MatchType::Fuzzy {
        should_query.push(rs_q::build_match("label.prefix", q.to_string())
            .with_boost(1000)
            .build());
    }

    if let &Some(ref c) = coord {
        // if we have coordinate, we boost we result near this coordinate
        let boost_on_proximity =
            rs_q::build_function_score()
                .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
                .with_boost(500)
                .with_function(Function::build_decay("coord",
                                           rs_u::Location::LatLon(c.lat, c.lon),
                                           rs_u::Distance::new(50f64,
                                                               rs_u::DistanceUnit::Kilometer))
                                   .build_exp())
                .build();
        should_query.push(boost_on_proximity);
    } else {
        // if we don't have coords, we take the field `weight` into account
        let boost_on_weight = rs_q::build_function_score()
            .with_boost_mode(rs_es::query::compound::BoostMode::Multiply)
            .with_boost(300)
            .with_query(rs_q::build_match_all().build())
            .with_function(Function::build_field_value_factor("weight")
                .with_factor(1)
                .with_modifier(rs_es::query::functions::Modifier::Log1p)
                .build())
            .build();
        should_query.push(boost_on_weight);
    }

    let sub_query = rs_q::build_bool()
        .with_should(should_query)
        .build();

    use rs_es::query::{CombinationMinimumShouldMatch, MinimumShouldMatch};
    let min_match = |min, threshold| {
        CombinationMinimumShouldMatch::new(MinimumShouldMatch::from(min),
                                           MinimumShouldMatch::from(threshold))
    };

    let minimum_should_match = match match_type {
        MatchType::Prefix => {
            // when we search [0, 2] fields, we need to match 100% of them
            // when we search [3, 4] fields, we need to match 90% of them
            // when we search [5, +] fields, we only need to match 70% on them
            // this way if the input has more fields than the documents
            // (additional information like the country, the region, ...)
            // they can be ignored
            MinimumShouldMatch::from(vec![min_match(2, 90f64), min_match(4, 70f64)])
        }
        // for fuzzy search we lower our expectation and we accept 50% of token match
        MatchType::Fuzzy => MinimumShouldMatch::from(50f64),
    };

    let mut must = vec![rs_q::build_multi_match(vec![main_match_type.to_string(),
                                                     "zip_codes.prefix".to_string()],
                                                q.to_string())
                            .with_minimum_should_match(minimum_should_match)
                            .build()];

    if let Some(s) = shape {
        must.push(rs_q::build_geo_polygon("coord", s).build());
    }

    // filter to handle house number
    // we either want:
    // * to exactly match the document house_number
    // * or that the document has no house_number
    let filter = rs_q::build_bool()
        .with_should(vec![rs_q::build_bool()
                              .with_must_not(rs_q::build_exists("house_number").build())
                              .build(),
                          rs_q::build_match("house_number", q.to_string()).build()])
        .with_must(must)
        .build();

    rs_q::build_bool()
        .with_must(vec![sub_query])
        .with_filter(filter)
        .build()
}

fn make_indexes<'a>(all_data: &Option<bool>,
                    pt_dataset_index: &'a String,
                    client: &mut rs_es::Client)
                    -> Vec<&'a str> {
    if *all_data == Some(true) {
        return vec!["munin"];
    }

    let mut result: Vec<&str> = vec![];

    if !pt_dataset_index.is_empty() {
        if client.open_index(&pt_dataset_index).is_ok() {
            result.push(pt_dataset_index);
        }
    }
    if client.open_index(&"munin_geo_data".to_string()).is_ok() {
        result.push("munin_geo_data");
    }
    return result;
}

fn query(q: &str,
         pt_dataset: &Option<&str>,
         all_data: &Option<bool>,
         cnx: &str,
         match_type: MatchType,
         offset: u64,
         limit: u64,
         coord: &Option<model::Coord>,
         shape: Option<Vec<rs_es::units::Location>>)
         -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    let query = build_query(q, match_type, coord, shape);

    let mut client = build_rs_client(&cnx.to_string());

    let pt_dataset_index = pt_dataset.map(|d| format!("munin_stop_{}", d))
        .unwrap_or("".to_string());
    let indexes = make_indexes(&all_data, &pt_dataset_index, &mut client);

    let result: SearchResult<serde_json::Value> = try!(client.search_query()
        .with_indexes(&indexes)
        .with_query(&query)
        .with_from(offset)
        .with_size(limit)
        .send());

    debug!("{} documents found", result.hits.total);

    // for the moment rs-es does not handle enum Document,
    // so we need to convert the ES glob to a Place
    Ok(result.hits
        .hits
        .into_iter()
        .filter_map(|hit| make_place(hit.doc_type, hit.source))
        .collect())
}

pub fn autocomplete(q: &str,
                    pt_dataset: &Option<&str>,
                    all_data: &Option<bool>,
                    offset: u64,
                    limit: u64,
                    coord: Option<model::Coord>,
                    cnx: &str,
                    shape: Option<Vec<(f64, f64)>>)
                    -> Result<Vec<mimir::Place>, rs_es::error::EsError> {
    fn make_shape(shape: &Option<Vec<(f64, f64)>>) -> Option<Vec<rs_es::units::Location>> {
        shape.as_ref().map(|v| v.iter().map(|&l| l.into()).collect())
    }

    // First we try a prety exact match on the prefix.
    // If there are no results then we do a new fuzzy search (matching ngrams)
    let results = try!(query(&q,
                             &pt_dataset,
                             &all_data,
                             cnx,
                             MatchType::Prefix,
                             offset,
                             limit,
                             &coord,
                             make_shape(&shape)));
    if results.is_empty() {
        query(&q,
              &pt_dataset,
              &all_data,
              cnx,
              MatchType::Fuzzy,
              offset,
              limit,
              &coord,
              make_shape(&shape))
    } else {
        Ok(results)
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "runtime")]
use lazy_static::lazy_static;
#[cfg(feature = "runtime")]
use tokio::runtime::{self, Runtime};

#[cfg(feature = "runtime")]
mod config;
mod client;
mod copy_out_reader;
mod portal;
mod query;
mod query_portal;
mod statement;
mod to_statement;
mod transaction;

#[cfg(feature = "runtime")]
#[cfg(test)]
mod test;

#[cfg(feature = "runtime")]
pub use crate::config::*;
pub use crate::client::*;
pub use crate::copy_out_reader::*;
pub use crate::portal::*;
pub use crate::query::*;
pub use crate::query_portal::*;
pub use crate::statement::*;
pub use crate::to_statement::*;
pub use crate::transaction::*;

#[cfg(feature = "runtime")]
lazy_static! {
    static ref RUNTIME: Runtime = runtime::Builder::new()
        .name_prefix("postgres-")
        .build()
        .unwrap();
}
<commit_msg>Rustfmt<commit_after>#[cfg(feature = "runtime")]
use lazy_static::lazy_static;
#[cfg(feature = "runtime")]
use tokio::runtime::{self, Runtime};

mod client;
#[cfg(feature = "runtime")]
mod config;
mod copy_out_reader;
mod portal;
mod query;
mod query_portal;
mod statement;
mod to_statement;
mod transaction;

#[cfg(feature = "runtime")]
#[cfg(test)]
mod test;

pub use crate::client::*;
#[cfg(feature = "runtime")]
pub use crate::config::*;
pub use crate::copy_out_reader::*;
pub use crate::portal::*;
pub use crate::query::*;
pub use crate::query_portal::*;
pub use crate::statement::*;
pub use crate::to_statement::*;
pub use crate::transaction::*;

#[cfg(feature = "runtime")]
lazy_static! {
    static ref RUNTIME: Runtime = runtime::Builder::new()
        .name_prefix("postgres-")
        .build()
        .unwrap();
}
<|endoftext|>