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

//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
//! It has to be run really early, before transformations like inlining, because
//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
//! of MIR building, and only after this pass we think of the program has having the
//! normal MIR semantics.

use rustc::ty::{self, TyCtxt, RegionKind};
use rustc::hir;
use rustc::mir::*;
use rustc::middle::region;
use transform::{MirPass, MirSource};

pub struct AddValidation;

/// Determine the "context" of the place: Mutability and region.
fn place_context<'a, 'tcx, D>(
    place: &Place<'tcx>,
    local_decls: &D,
    tcx: TyCtxt<'a, 'tcx, 'tcx>
) -> (Option<region::Scope>, hir::Mutability)
    where D: HasLocalDecls<'tcx>
{
    use rustc::mir::Place::*;

    match *place {
        Local { .. } => (None, hir::MutMutable),
        Static(_) => (None, hir::MutImmutable),
        Projection(ref proj) => {
            match proj.elem {
                ProjectionElem::Deref => {
                    // Computing the inside the recursion makes this quadratic.
                    // We don't expect deep paths though.
                    let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
                    // A Deref projection may restrict the context, this depends on the type
                    // being deref'd.
                    let context = match ty.sty {
                        ty::TyRef(re, _, mutbl) => {
                            let re = match re {
                                &RegionKind::ReScope(ce) => Some(ce),
                                &RegionKind::ReErased =>
                                    bug!("AddValidation pass must be run before erasing lifetimes"),
                                _ => None
                            };
                            (re, mutbl)
                        }
                        ty::TyRawPtr(_) =>
                            // There is no guarantee behind even a mutable raw pointer,
                            // no write locks are acquired there, so we also don't want to
                            // release any.
                            (None, hir::MutImmutable),
                        ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable),
                        _ => bug!("Deref on a non-pointer type {:?}", ty),
                    };
                    // "Intersect" this restriction with proj.base.
                    if let (Some(_), hir::MutImmutable) = context {
                        // This is already as restricted as it gets, no need to even recurse
                        context
                    } else {
                        let base_context = place_context(&proj.base, local_decls, tcx);
                        // The region of the outermost Deref is always most restrictive.
                        let re = context.0.or(base_context.0);
                        let mutbl = context.1.and(base_context.1);
                        (re, mutbl)
                    }

                }
                _ => place_context(&proj.base, local_decls, tcx),
            }
        }
    }
}

/// Check if this function contains an unsafe block or is an unsafe function.
fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool {
    use rustc::hir::intravisit::{self, Visitor, FnKind};
    use rustc::hir::map::blocks::FnLikeNode;
    use rustc::hir::map::Node;

    /// Decide if this is an unsafe block
    fn block_is_unsafe(block: &hir::Block) -> bool {
        use rustc::hir::BlockCheckMode::*;

        match block.rules {
            UnsafeBlock(_) | PushUnsafeBlock(_) => true,
            // For PopUnsafeBlock, we don't actually know -- but we will always also check all
            // parent blocks, so we can safely declare the PopUnsafeBlock to not be unsafe.
            DefaultBlock | PopUnsafeBlock(_) => false,
        }
    }

    /// Decide if this FnLike is a closure
    fn fn_is_closure<'a>(fn_like: FnLikeNode<'a>) -> bool {
        match fn_like.kind() {
            FnKind::Closure(_) => true,
            FnKind::Method(..) | FnKind::ItemFn(..) => false,
        }
    }

    let node_id = tcx.hir.as_local_node_id(src.def_id).unwrap();
    let fn_like = match tcx.hir.body_owner_kind(node_id) {
        hir::BodyOwnerKind::Fn => {
            match FnLikeNode::from_node(tcx.hir.get(node_id)) {
                Some(fn_like) => fn_like,
                None => return false, // e.g. struct ctor shims -- such auto-generated code cannot
                                      // contain unsafe.
            }
        },
        _ => return false, // only functions can have unsafe
    };

    // Test if the function is marked unsafe.
    if fn_like.unsafety() == hir::Unsafety::Unsafe {
        return true;
    }

    // For closures, we need to walk up the parents and see if we are inside an unsafe fn or
    // unsafe block.
    if fn_is_closure(fn_like) {
        let mut cur = fn_like.id();
        loop {
            // Go further upwards.
            cur = tcx.hir.get_parent_node(cur);
            let node = tcx.hir.get(cur);
            // Check if this is an unsafe function
            if let Some(fn_like) = FnLikeNode::from_node(node) {
                if !fn_is_closure(fn_like) {
                    if fn_like.unsafety() == hir::Unsafety::Unsafe {
                        return true;
                    }
                }
            }
            // Check if this is an unsafe block, or an item
            match node {
                Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => {
                    if block_is_unsafe(&*block) {
                        // Found an unsafe block, we can bail out here.
                        return true;
                    }
                }
                Node::NodeItem(..) => {
                    // No walking up beyond items.  This makes sure the loop always terminates.
                    break;
                }
                _ => {},
            }
        }
    }

    // Visit the entire body of the function and check for unsafe blocks in there
    struct FindUnsafe {
        found_unsafe: bool,
    }
    let mut finder = FindUnsafe { found_unsafe: false };
    // Run the visitor on the NodeId we got.  Seems like there is no uniform way to do that.
    finder.visit_body(tcx.hir.body(fn_like.body()));

    impl<'tcx> Visitor<'tcx> for FindUnsafe {
        fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_block(&mut self, b: &'tcx hir::Block) {
            if self.found_unsafe { return; } // short-circuit

            if block_is_unsafe(b) {
                // We found an unsafe block.  We can stop searching.
                self.found_unsafe = true;
            } else {
                // No unsafe block here, go on searching.
                intravisit::walk_block(self, b);
            }
        }
    }

    finder.found_unsafe
}

impl MirPass for AddValidation {
    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          src: MirSource,
                          mir: &mut Mir<'tcx>)
    {
        let emit_validate = tcx.sess.opts.debugging_opts.mir_emit_validate;
        if emit_validate == 0 {
            return;
        }
        let restricted_validation = emit_validate == 1 && fn_contains_unsafe(tcx, src);
        let local_decls = mir.local_decls.clone(); // FIXME: Find a way to get rid of this clone.

        // Convert a place to a validation operand.
        let place_to_operand = |place: Place<'tcx>| -> ValidationOperand<'tcx, Place<'tcx>> {
            let (re, mutbl) = place_context(&place, &local_decls, tcx);
            let ty = place.ty(&local_decls, tcx).to_ty(tcx);
            ValidationOperand { place, ty, re, mutbl }
        };

        // Emit an Acquire at the beginning of the given block.  If we are in restricted emission
        // mode (mir_emit_validate=1), also emit a Release immediately after the Acquire.
        let emit_acquire = |block: &mut BasicBlockData<'tcx>, source_info, operands: Vec<_>| {
            if operands.len() == 0 {
                return; // Nothing to do
            }
            // Emit the release first, to avoid cloning if we do not emit it
            if restricted_validation {
                let release_stmt = Statement {
                    source_info,
                    kind: StatementKind::Validate(ValidationOp::Release, operands.clone()),
                };
                block.statements.insert(0, release_stmt);
            }
            // Now, the acquire
            let acquire_stmt = Statement {
                source_info,
                kind: StatementKind::Validate(ValidationOp::Acquire, operands),
            };
            block.statements.insert(0, acquire_stmt);
        };

        // PART 1
        // Add an AcquireValid at the beginning of the start block.
        {
            let source_info = SourceInfo {
                scope: OUTERMOST_SOURCE_SCOPE,
                span: mir.span, // FIXME: Consider using just the span covering the function
                                // argument declaration.
            };
            // Gather all arguments, skip return value.
            let operands = mir.local_decls.iter_enumerated().skip(1).take(mir.arg_count)
                    .map(|(local, _)| place_to_operand(Place::Local(local))).collect();
            emit_acquire(&mut mir.basic_blocks_mut()[START_BLOCK], source_info, operands);
        }

        // PART 2
        // Add ReleaseValid/AcquireValid around function call terminators.  We don't use a visitor
        // because we need to access the block that a Call jumps to.
        let mut returns : Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
        for block_data in mir.basic_blocks_mut() {
            match block_data.terminator {
                Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments *and* the return value.
                    // The callee may write into the return value!  Note that this relies
                    // on "release of uninitialized" to be a NOP.
                    if !restricted_validation {
                        let release_stmt = Statement {
                            source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                destination.iter().map(|dest| place_to_operand(dest.0.clone()))
                                .chain(
                                    args.iter().filter_map(|op| {
                                        match op {
                                            &Operand::Copy(ref place) |
                                            &Operand::Move(ref place) =>
                                                Some(place_to_operand(place.clone())),
                                            &Operand::Constant(..) => { None },
                                        }
                                    })
                                ).collect())
                        };
                        block_data.statements.push(release_stmt);
                    }
                    // Remember the return destination for later
                    if let &Some(ref destination) = destination {
                        returns.push((source_info, destination.0.clone(), destination.1));
                    }
                }
                Some(Terminator { kind: TerminatorKind::Drop { location: ref place, .. },
                                  source_info }) |
                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref place, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    if !restricted_validation {
                        let release_stmt = Statement {
                            source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                    vec![place_to_operand(place.clone())]),
                        };
                        block_data.statements.push(release_stmt);
                    }
                    // drop doesn't return anything, so we need no acquire.
                }
                _ => {
                    // Not a block ending in a Call -> ignore.
                }
            }
        }
        // Now we go over the returns we collected to acquire the return values.
        for (source_info, dest_place, dest_block) in returns {
            emit_acquire(
                &mut mir.basic_blocks_mut()[dest_block],
                source_info,
                vec![place_to_operand(dest_place)]
            );
        }

        if restricted_validation {
            // No part 3 for us.
            return;
        }

        // PART 3
        // Add ReleaseValid/AcquireValid around Ref and Cast.  Again an iterator does not seem very
        // suited as we need to add new statements before and after each Ref.
        for block_data in mir.basic_blocks_mut() {
            // We want to insert statements around Ref commands as we iterate.  To this end, we
            // iterate backwards using indices.
            for i in (0..block_data.statements.len()).rev() {
                match block_data.statements[i].kind {
                    // When the borrow of this ref expires, we need to recover validation.
                    StatementKind::Assign(_, Rvalue::Ref(_, _, _)) => {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_place, re, src_place) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_place,
                                                  Rvalue::Ref(re, _, ref src_place)) => {
                                (dest_place.clone(), re, src_place.clone())
                            },
                            _ => bug!("We already matched this."),
                        };
                        // So this is a ref, and we got all the data we wanted.
                        // Do an acquire of the result -- but only what it points to, so add a Deref
                        // projection.
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![place_to_operand(dest_place.deref())]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // The source is released until the region of the borrow ends.
                        let op = match re {
                            &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
                            &RegionKind::ReErased =>
                                bug!("AddValidation pass must be run before erasing lifetimes"),
                            _ => ValidationOp::Release,
                        };
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(op, vec![place_to_operand(src_place)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    // Casts can change what validation does (e.g. unsizing)
                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Copy(_), _)) |
                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Move(_), _))
                        if kind != CastKind::Misc =>
                    {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_place, src_place) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_place,
                                    Rvalue::Cast(_, Operand::Copy(ref src_place), _)) |
                            StatementKind::Assign(ref dest_place,
                                    Rvalue::Cast(_, Operand::Move(ref src_place), _)) =>
                            {
                                (dest_place.clone(), src_place.clone())
                            },
                            _ => bug!("We already matched this."),
                        };

                        // Acquire of the result
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![place_to_operand(dest_place)]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // Release of the input
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                                            vec![place_to_operand(src_place)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    _ => {},
                }
            }
        }
    }
}
<commit_msg>Remove a clone in mir/transform/add_validation.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This pass adds validation calls (AcquireValid, ReleaseValid) where appropriate.
//! It has to be run really early, before transformations like inlining, because
//! introducing these calls *adds* UB -- so, conceptually, this pass is actually part
//! of MIR building, and only after this pass we think of the program has having the
//! normal MIR semantics.

use rustc::ty::{self, TyCtxt, RegionKind};
use rustc::hir;
use rustc::mir::*;
use rustc::middle::region;
use transform::{MirPass, MirSource};

pub struct AddValidation;

/// Determine the "context" of the place: Mutability and region.
fn place_context<'a, 'tcx, D>(
    place: &Place<'tcx>,
    local_decls: &D,
    tcx: TyCtxt<'a, 'tcx, 'tcx>
) -> (Option<region::Scope>, hir::Mutability)
    where D: HasLocalDecls<'tcx>
{
    use rustc::mir::Place::*;

    match *place {
        Local { .. } => (None, hir::MutMutable),
        Static(_) => (None, hir::MutImmutable),
        Projection(ref proj) => {
            match proj.elem {
                ProjectionElem::Deref => {
                    // Computing the inside the recursion makes this quadratic.
                    // We don't expect deep paths though.
                    let ty = proj.base.ty(local_decls, tcx).to_ty(tcx);
                    // A Deref projection may restrict the context, this depends on the type
                    // being deref'd.
                    let context = match ty.sty {
                        ty::TyRef(re, _, mutbl) => {
                            let re = match re {
                                &RegionKind::ReScope(ce) => Some(ce),
                                &RegionKind::ReErased =>
                                    bug!("AddValidation pass must be run before erasing lifetimes"),
                                _ => None
                            };
                            (re, mutbl)
                        }
                        ty::TyRawPtr(_) =>
                            // There is no guarantee behind even a mutable raw pointer,
                            // no write locks are acquired there, so we also don't want to
                            // release any.
                            (None, hir::MutImmutable),
                        ty::TyAdt(adt, _) if adt.is_box() => (None, hir::MutMutable),
                        _ => bug!("Deref on a non-pointer type {:?}", ty),
                    };
                    // "Intersect" this restriction with proj.base.
                    if let (Some(_), hir::MutImmutable) = context {
                        // This is already as restricted as it gets, no need to even recurse
                        context
                    } else {
                        let base_context = place_context(&proj.base, local_decls, tcx);
                        // The region of the outermost Deref is always most restrictive.
                        let re = context.0.or(base_context.0);
                        let mutbl = context.1.and(base_context.1);
                        (re, mutbl)
                    }

                }
                _ => place_context(&proj.base, local_decls, tcx),
            }
        }
    }
}

/// Check if this function contains an unsafe block or is an unsafe function.
fn fn_contains_unsafe<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource) -> bool {
    use rustc::hir::intravisit::{self, Visitor, FnKind};
    use rustc::hir::map::blocks::FnLikeNode;
    use rustc::hir::map::Node;

    /// Decide if this is an unsafe block
    fn block_is_unsafe(block: &hir::Block) -> bool {
        use rustc::hir::BlockCheckMode::*;

        match block.rules {
            UnsafeBlock(_) | PushUnsafeBlock(_) => true,
            // For PopUnsafeBlock, we don't actually know -- but we will always also check all
            // parent blocks, so we can safely declare the PopUnsafeBlock to not be unsafe.
            DefaultBlock | PopUnsafeBlock(_) => false,
        }
    }

    /// Decide if this FnLike is a closure
    fn fn_is_closure<'a>(fn_like: FnLikeNode<'a>) -> bool {
        match fn_like.kind() {
            FnKind::Closure(_) => true,
            FnKind::Method(..) | FnKind::ItemFn(..) => false,
        }
    }

    let node_id = tcx.hir.as_local_node_id(src.def_id).unwrap();
    let fn_like = match tcx.hir.body_owner_kind(node_id) {
        hir::BodyOwnerKind::Fn => {
            match FnLikeNode::from_node(tcx.hir.get(node_id)) {
                Some(fn_like) => fn_like,
                None => return false, // e.g. struct ctor shims -- such auto-generated code cannot
                                      // contain unsafe.
            }
        },
        _ => return false, // only functions can have unsafe
    };

    // Test if the function is marked unsafe.
    if fn_like.unsafety() == hir::Unsafety::Unsafe {
        return true;
    }

    // For closures, we need to walk up the parents and see if we are inside an unsafe fn or
    // unsafe block.
    if fn_is_closure(fn_like) {
        let mut cur = fn_like.id();
        loop {
            // Go further upwards.
            cur = tcx.hir.get_parent_node(cur);
            let node = tcx.hir.get(cur);
            // Check if this is an unsafe function
            if let Some(fn_like) = FnLikeNode::from_node(node) {
                if !fn_is_closure(fn_like) {
                    if fn_like.unsafety() == hir::Unsafety::Unsafe {
                        return true;
                    }
                }
            }
            // Check if this is an unsafe block, or an item
            match node {
                Node::NodeExpr(&hir::Expr { node: hir::ExprBlock(ref block, _), ..}) => {
                    if block_is_unsafe(&*block) {
                        // Found an unsafe block, we can bail out here.
                        return true;
                    }
                }
                Node::NodeItem(..) => {
                    // No walking up beyond items.  This makes sure the loop always terminates.
                    break;
                }
                _ => {},
            }
        }
    }

    // Visit the entire body of the function and check for unsafe blocks in there
    struct FindUnsafe {
        found_unsafe: bool,
    }
    let mut finder = FindUnsafe { found_unsafe: false };
    // Run the visitor on the NodeId we got.  Seems like there is no uniform way to do that.
    finder.visit_body(tcx.hir.body(fn_like.body()));

    impl<'tcx> Visitor<'tcx> for FindUnsafe {
        fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
            intravisit::NestedVisitorMap::None
        }

        fn visit_block(&mut self, b: &'tcx hir::Block) {
            if self.found_unsafe { return; } // short-circuit

            if block_is_unsafe(b) {
                // We found an unsafe block.  We can stop searching.
                self.found_unsafe = true;
            } else {
                // No unsafe block here, go on searching.
                intravisit::walk_block(self, b);
            }
        }
    }

    finder.found_unsafe
}

impl MirPass for AddValidation {
    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          src: MirSource,
                          mir: &mut Mir<'tcx>)
    {
        let emit_validate = tcx.sess.opts.debugging_opts.mir_emit_validate;
        if emit_validate == 0 {
            return;
        }
        let restricted_validation = emit_validate == 1 && fn_contains_unsafe(tcx, src);
        let (span, arg_count) = (mir.span, mir.arg_count);
        let (basic_blocks, local_decls) = mir.basic_blocks_and_local_decls_mut();

        // Convert a place to a validation operand.
        let place_to_operand = |place: Place<'tcx>| -> ValidationOperand<'tcx, Place<'tcx>> {
            let (re, mutbl) = place_context(&place, local_decls, tcx);
            let ty = place.ty(local_decls, tcx).to_ty(tcx);
            ValidationOperand { place, ty, re, mutbl }
        };

        // Emit an Acquire at the beginning of the given block.  If we are in restricted emission
        // mode (mir_emit_validate=1), also emit a Release immediately after the Acquire.
        let emit_acquire = |block: &mut BasicBlockData<'tcx>, source_info, operands: Vec<_>| {
            if operands.len() == 0 {
                return; // Nothing to do
            }
            // Emit the release first, to avoid cloning if we do not emit it
            if restricted_validation {
                let release_stmt = Statement {
                    source_info,
                    kind: StatementKind::Validate(ValidationOp::Release, operands.clone()),
                };
                block.statements.insert(0, release_stmt);
            }
            // Now, the acquire
            let acquire_stmt = Statement {
                source_info,
                kind: StatementKind::Validate(ValidationOp::Acquire, operands),
            };
            block.statements.insert(0, acquire_stmt);
        };

        // PART 1
        // Add an AcquireValid at the beginning of the start block.
        {
            let source_info = SourceInfo {
                scope: OUTERMOST_SOURCE_SCOPE,
                span: span, // FIXME: Consider using just the span covering the function
                            // argument declaration.
            };
            // Gather all arguments, skip return value.
            let operands = local_decls.iter_enumerated().skip(1).take(arg_count)
                    .map(|(local, _)| place_to_operand(Place::Local(local))).collect();
            emit_acquire(&mut basic_blocks[START_BLOCK], source_info, operands);
        }

        // PART 2
        // Add ReleaseValid/AcquireValid around function call terminators.  We don't use a visitor
        // because we need to access the block that a Call jumps to.
        let mut returns : Vec<(SourceInfo, Place<'tcx>, BasicBlock)> = Vec::new();
        for block_data in basic_blocks.iter_mut() {
            match block_data.terminator {
                Some(Terminator { kind: TerminatorKind::Call { ref args, ref destination, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments *and* the return value.
                    // The callee may write into the return value!  Note that this relies
                    // on "release of uninitialized" to be a NOP.
                    if !restricted_validation {
                        let release_stmt = Statement {
                            source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                destination.iter().map(|dest| place_to_operand(dest.0.clone()))
                                .chain(
                                    args.iter().filter_map(|op| {
                                        match op {
                                            &Operand::Copy(ref place) |
                                            &Operand::Move(ref place) =>
                                                Some(place_to_operand(place.clone())),
                                            &Operand::Constant(..) => { None },
                                        }
                                    })
                                ).collect())
                        };
                        block_data.statements.push(release_stmt);
                    }
                    // Remember the return destination for later
                    if let &Some(ref destination) = destination {
                        returns.push((source_info, destination.0.clone(), destination.1));
                    }
                }
                Some(Terminator { kind: TerminatorKind::Drop { location: ref place, .. },
                                  source_info }) |
                Some(Terminator { kind: TerminatorKind::DropAndReplace { location: ref place, .. },
                                  source_info }) => {
                    // Before the call: Release all arguments
                    if !restricted_validation {
                        let release_stmt = Statement {
                            source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                    vec![place_to_operand(place.clone())]),
                        };
                        block_data.statements.push(release_stmt);
                    }
                    // drop doesn't return anything, so we need no acquire.
                }
                _ => {
                    // Not a block ending in a Call -> ignore.
                }
            }
        }
        // Now we go over the returns we collected to acquire the return values.
        for (source_info, dest_place, dest_block) in returns {
            emit_acquire(
                &mut basic_blocks[dest_block],
                source_info,
                vec![place_to_operand(dest_place)]
            );
        }

        if restricted_validation {
            // No part 3 for us.
            return;
        }

        // PART 3
        // Add ReleaseValid/AcquireValid around Ref and Cast.  Again an iterator does not seem very
        // suited as we need to add new statements before and after each Ref.
        for block_data in basic_blocks {
            // We want to insert statements around Ref commands as we iterate.  To this end, we
            // iterate backwards using indices.
            for i in (0..block_data.statements.len()).rev() {
                match block_data.statements[i].kind {
                    // When the borrow of this ref expires, we need to recover validation.
                    StatementKind::Assign(_, Rvalue::Ref(_, _, _)) => {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_place, re, src_place) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_place,
                                                  Rvalue::Ref(re, _, ref src_place)) => {
                                (dest_place.clone(), re, src_place.clone())
                            },
                            _ => bug!("We already matched this."),
                        };
                        // So this is a ref, and we got all the data we wanted.
                        // Do an acquire of the result -- but only what it points to, so add a Deref
                        // projection.
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![place_to_operand(dest_place.deref())]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // The source is released until the region of the borrow ends.
                        let op = match re {
                            &RegionKind::ReScope(ce) => ValidationOp::Suspend(ce),
                            &RegionKind::ReErased =>
                                bug!("AddValidation pass must be run before erasing lifetimes"),
                            _ => ValidationOp::Release,
                        };
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(op, vec![place_to_operand(src_place)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    // Casts can change what validation does (e.g. unsizing)
                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Copy(_), _)) |
                    StatementKind::Assign(_, Rvalue::Cast(kind, Operand::Move(_), _))
                        if kind != CastKind::Misc =>
                    {
                        // Due to a lack of NLL; we can't capture anything directly here.
                        // Instead, we have to re-match and clone there.
                        let (dest_place, src_place) = match block_data.statements[i].kind {
                            StatementKind::Assign(ref dest_place,
                                    Rvalue::Cast(_, Operand::Copy(ref src_place), _)) |
                            StatementKind::Assign(ref dest_place,
                                    Rvalue::Cast(_, Operand::Move(ref src_place), _)) =>
                            {
                                (dest_place.clone(), src_place.clone())
                            },
                            _ => bug!("We already matched this."),
                        };

                        // Acquire of the result
                        let acquire_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Acquire,
                                    vec![place_to_operand(dest_place)]),
                        };
                        block_data.statements.insert(i+1, acquire_stmt);

                        // Release of the input
                        let release_stmt = Statement {
                            source_info: block_data.statements[i].source_info,
                            kind: StatementKind::Validate(ValidationOp::Release,
                                                            vec![place_to_operand(src_place)]),
                        };
                        block_data.statements.insert(i, release_stmt);
                    }
                    _ => {},
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate osmpbfreader;
extern crate time;

use time::PreciseTime;
use std::fs::File;
use std::path::Path;
use std::ffi::OsString;
use std::collections::HashMap;
use std::collections::HashSet;

use osmpbfreader::OsmObj;
use osmpbfreader::OsmPbfReader;

#[derive(Debug, Clone, Copy)]
struct Position {
    lat: f64,
    lon: f64,
}

#[derive(Debug, Clone, Copy)]
struct ParsedEdge {
    id_from: i64,
    id_to: i64,
    length: f64,
}

struct ParseData {
    // used node-ids
    nodes_used: HashSet<i64>,
    // "useful" ways
    filtered_ways: HashSet<i64>,
    // relevant nodes and their position
    nodes: HashMap<i64, Position>,
    // edges
    edges: Vec<ParsedEdge>
}

#[derive(Debug)]
struct RoutingEdge {
    target: usize,
    length: f64,
    constraints: u8,
}

struct OsmNode {
    position: Position,
    internal_id: usize
}

struct RoutingData {
    // relevant nodes and their position
    osm_nodes: HashMap<i64, OsmNode>,
    //[n_id] -> osm_n_id
    internal_nodes: Vec<i64>,
    // [e_id] -> n_id target|length|constraints
    internal_edges: Vec<RoutingEdge>,
    // [n_id] -> e_id
    internal_offset: Vec<usize>,
}

fn main() {
    let default_file = OsString::from("/home/zsdn/baden-wuerttemberg-latest.osm.pbf".to_string());
    //let default_file = OsString::from("/home/zsdn/germany-latest.osm.pbf".to_string());
    let args: Vec<OsString> = std::env::args_os().collect();
    match args.len() {
        1 => {
            read_file(&default_file);
        }
        2 => {
            read_file(&args[1]);
        }
        _ => {},
    };
}

fn read_file(filename: &OsString) -> RoutingData {
    let mut parse_result = ParseData { nodes_used: HashSet::new(), filtered_ways: HashSet::new(), nodes: HashMap::new(), edges: Vec::new() };

    let start_p1 = PreciseTime::now();
    first_parse(&filename, &mut parse_result);
    let end_p1 = PreciseTime::now();

    println!("P1 | ways:  {}", parse_result.filtered_ways.len());
    println!("P1 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P1 | edges: {}", parse_result.edges.len());
    println!("P1 | nodes: {}", parse_result.nodes.len());
    println!("P1 | duration: {}", start_p1.to(end_p1));

    let start_p2 = PreciseTime::now();
    second_parse(&filename, &mut parse_result);
    let end_p2 = PreciseTime::now();

    println!("P2 | ways:  {}", parse_result.filtered_ways.len());
    println!("P2 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P2 | edges: {}", parse_result.edges.len());
    println!("P2 | nodes: {}", parse_result.nodes.len());
    println!("P2 | duration: {}", start_p2.to(end_p2));

    let start_p3 = PreciseTime::now();
    third_parse(&filename, &mut parse_result);
    let end_p3 = PreciseTime::now();

    println!("P3 | ways:  {}", parse_result.filtered_ways.len());
    println!("P3 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P3 | edges: {}", parse_result.edges.len());
    println!("P3 | nodes: {}", parse_result.nodes.len());
    println!("P3 | duration: {}", start_p3.to(end_p3));

    let start_b = PreciseTime::now();
    let routing_data = build_routing_data(parse_result);
    let end_b = PreciseTime::now();

    println!("B  | edges:  {}", routing_data.internal_edges.len());
    println!("B  | nodes:  {}", routing_data.internal_nodes.len());
    println!("B  | offset:  {}", routing_data.internal_offset.len());
    println!("B  | osm_nodes:  {}", routing_data.osm_nodes.len());
    println!("B  | duration: {}", start_b.to(end_b));

    return routing_data;
}

#[test]
fn test() {
    let edge_vec = vec![ParsedEdge{id_from : 0, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 0, id_to : 2, length : 1.0},
                        ParsedEdge{id_from : 2, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 2, id_to : 3, length : 1.0},
                        ParsedEdge{id_from : 3, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 3, id_to : 4, length : 1.0},
    ];

    let mut nodes_map = HashMap::new();

    nodes_map.insert(0, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(1, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(2, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(3, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(4, Position { lat: 0.0, lon: 0.0 });

    let parse_result = ParseData { nodes: nodes_map, edges: edge_vec, filtered_ways: HashSet::new(), nodes_used: HashSet::new() };

    let routing_data = build_routing_data(parse_result);

    println!("NODES: {:?}", routing_data.internal_nodes);
    println!("EDGES: {:?}", routing_data.internal_edges);
    println!("OFFSET: {:?}", routing_data.internal_offset);

    assert_eq!(routing_data.internal_offset, vec![0, 2, 2, 4, 5]);
}

fn first_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();

    let mut invalid_values = HashSet::new();
    init_filter_list(&mut invalid_values);

    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Way(way) => {
                if filter_way(&way, &invalid_values) {
                    for node in way.nodes {
                        parse_result.nodes_used.insert(node);
                    }
                    parse_result.filtered_ways.insert(way.id);
                }
            }
            _ => {}
        }
    }
}

fn second_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();
    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Node(node) => {
                if parse_result.nodes_used.remove(&node.id) {
                    parse_result.nodes.insert(node.id, Position { lat: node.lat, lon: node.lon });
                }
            }
            _ => {}
        }
    }
}

fn third_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();
    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Way(way) => {
                if parse_result.filtered_ways.remove(&way.id) {
                    for node_pair in way.nodes.windows(2) {
                        if let (Some(from), Some(to)) = (node_pair.first(), node_pair.last()) {
                            if let (Some(from_node), Some(to_node)) = (parse_result.nodes.get(from), parse_result.nodes.get(to)) {
                                let edge_length = calculate_distance(from_node, to_node);
                                let edge = ParsedEdge { id_from: from.clone(), id_to: to.clone(), length: edge_length.clone() };
                                parse_result.edges.push(edge);

                                if let Some(val) = way.tags.get("oneway") {
                                    if val == "yes" {
                                        let edge_reverse = ParsedEdge { id_from: to.clone(), id_to: from.clone(), length: edge_length.clone() };
                                        parse_result.edges.push(edge_reverse);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
}

fn build_routing_data(mut parse_result: ParseData) -> RoutingData {
    let mut routing_data = RoutingData { osm_nodes: HashMap::new(), internal_nodes: Vec::new(), internal_edges: Vec::new(), internal_offset: vec![usize::max_value(); parse_result.nodes.len()] };

    parse_result.edges.sort_by(|a, b| b.id_from.cmp(&a.id_from));

    for node in parse_result.nodes.keys() {
        routing_data.internal_nodes.push(node.clone());
    }

    routing_data.internal_nodes.sort();

    for (i, node) in routing_data.internal_nodes.iter().enumerate() {
        if let Some(pos) = parse_result.nodes.remove(node) {
            routing_data.osm_nodes.insert(node.clone(), OsmNode { position: pos, internal_id: i });

            if let Some(edge) = parse_result.edges.last() {
                if edge.id_from == *node {
                    routing_data.internal_offset[i] = routing_data.internal_edges.len();
                }
            }

            loop {
                if let Some(edge) = parse_result.edges.last() {
                    if edge.id_from != *node {
                        break;
                    }
                } else {
                    break;
                }
                if let Some(edge) = parse_result.edges.pop() {
                    routing_data.internal_edges.push(RoutingEdge { target: edge.id_to as usize, length: edge.length, constraints: 0b00000001 });
                } else {
                    break;
                }
            }
        }
    }

    let mut current_offset = routing_data.internal_edges.len() - 1;

    for offset in &mut routing_data.internal_offset.iter_mut().rev() {
        if *offset == usize::max_value() {
            *offset = current_offset;
        } else {
            current_offset = *offset;
        }
    }

    return routing_data;
}

fn calculate_distance(node1: &Position, node2: &Position) -> f64 {
    let lat1 = node1.lat;
    let lat2 = node2.lat;
    let lng1 = node1.lon;
    let lng2 = node2.lon;

    let earth_radius: f64 = 6371000.0; //meters
    let d_lat = (lat2 - lat1).to_radians();
    let d_lng = (lng2 - lng1).to_radians();
    let a = (d_lat / 2.0).sin() * (d_lat / 2.0).sin() + lat1.to_radians().cos() * lat2.to_radians().cos() * (d_lng / 2.0).sin() * (d_lng / 2.0).sin();
    let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
    let dist = earth_radius * c;

    return dist;
}

fn init_filter_list(invalid_values: &mut HashSet<&str>) {
    invalid_values.insert("services");
    invalid_values.insert("pedestrian");
    invalid_values.insert("raceway");
    invalid_values.insert("footway");
    invalid_values.insert("path");
    invalid_values.insert("steps");
    invalid_values.insert("bridleway");
    invalid_values.insert("construction");
}

fn filter_way(way: &osmpbfreader::Way, invalid_values: &HashSet<&str>) -> bool {
    if let Some(value) = way.tags.get("highway") {
        return !invalid_values.contains(&value.as_str());
    } else {
        return false;
    }
}<commit_msg>rename test<commit_after>extern crate osmpbfreader;
extern crate time;

use time::PreciseTime;
use std::fs::File;
use std::path::Path;
use std::ffi::OsString;
use std::collections::HashMap;
use std::collections::HashSet;

use osmpbfreader::OsmObj;
use osmpbfreader::OsmPbfReader;

#[derive(Debug, Clone, Copy)]
struct Position {
    lat: f64,
    lon: f64,
}

#[derive(Debug, Clone, Copy)]
struct ParsedEdge {
    id_from: i64,
    id_to: i64,
    length: f64,
}

struct ParseData {
    // used node-ids
    nodes_used: HashSet<i64>,
    // "useful" ways
    filtered_ways: HashSet<i64>,
    // relevant nodes and their position
    nodes: HashMap<i64, Position>,
    // edges
    edges: Vec<ParsedEdge>
}

#[derive(Debug)]
struct RoutingEdge {
    target: usize,
    length: f64,
    constraints: u8,
}

struct OsmNode {
    position: Position,
    internal_id: usize
}

struct RoutingData {
    // relevant nodes and their position
    osm_nodes: HashMap<i64, OsmNode>,
    //[n_id] -> osm_n_id
    internal_nodes: Vec<i64>,
    // [e_id] -> n_id target|length|constraints
    internal_edges: Vec<RoutingEdge>,
    // [n_id] -> e_id
    internal_offset: Vec<usize>,
}

fn main() {
    let default_file = OsString::from("/home/zsdn/baden-wuerttemberg-latest.osm.pbf".to_string());
    //let default_file = OsString::from("/home/zsdn/germany-latest.osm.pbf".to_string());
    let args: Vec<OsString> = std::env::args_os().collect();
    match args.len() {
        1 => {
            read_file(&default_file);
        }
        2 => {
            read_file(&args[1]);
        }
        _ => {},
    };
}

fn read_file(filename: &OsString) -> RoutingData {
    let mut parse_result = ParseData { nodes_used: HashSet::new(), filtered_ways: HashSet::new(), nodes: HashMap::new(), edges: Vec::new() };

    let start_p1 = PreciseTime::now();
    first_parse(&filename, &mut parse_result);
    let end_p1 = PreciseTime::now();

    println!("P1 | ways:  {}", parse_result.filtered_ways.len());
    println!("P1 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P1 | edges: {}", parse_result.edges.len());
    println!("P1 | nodes: {}", parse_result.nodes.len());
    println!("P1 | duration: {}", start_p1.to(end_p1));

    let start_p2 = PreciseTime::now();
    second_parse(&filename, &mut parse_result);
    let end_p2 = PreciseTime::now();

    println!("P2 | ways:  {}", parse_result.filtered_ways.len());
    println!("P2 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P2 | edges: {}", parse_result.edges.len());
    println!("P2 | nodes: {}", parse_result.nodes.len());
    println!("P2 | duration: {}", start_p2.to(end_p2));

    let start_p3 = PreciseTime::now();
    third_parse(&filename, &mut parse_result);
    let end_p3 = PreciseTime::now();

    println!("P3 | ways:  {}", parse_result.filtered_ways.len());
    println!("P3 | nodes_used: {}", parse_result.nodes_used.len());
    println!("P3 | edges: {}", parse_result.edges.len());
    println!("P3 | nodes: {}", parse_result.nodes.len());
    println!("P3 | duration: {}", start_p3.to(end_p3));

    let start_b = PreciseTime::now();
    let routing_data = build_routing_data(parse_result);
    let end_b = PreciseTime::now();

    println!("B  | edges:  {}", routing_data.internal_edges.len());
    println!("B  | nodes:  {}", routing_data.internal_nodes.len());
    println!("B  | offset:  {}", routing_data.internal_offset.len());
    println!("B  | osm_nodes:  {}", routing_data.osm_nodes.len());
    println!("B  | duration: {}", start_b.to(end_b));

    return routing_data;
}

#[test]
fn test_routing_data_gen() {
    let edge_vec = vec![ParsedEdge{id_from : 0, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 0, id_to : 2, length : 1.0},
                        ParsedEdge{id_from : 2, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 2, id_to : 3, length : 1.0},
                        ParsedEdge{id_from : 3, id_to : 1, length : 1.0},
                        ParsedEdge{id_from : 3, id_to : 4, length : 1.0},
    ];

    let mut nodes_map = HashMap::new();

    nodes_map.insert(0, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(1, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(2, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(3, Position { lat: 0.0, lon: 0.0 });
    nodes_map.insert(4, Position { lat: 0.0, lon: 0.0 });

    let parse_result = ParseData { nodes: nodes_map, edges: edge_vec, filtered_ways: HashSet::new(), nodes_used: HashSet::new() };

    let routing_data = build_routing_data(parse_result);

    println!("NODES: {:?}", routing_data.internal_nodes);
    println!("EDGES: {:?}", routing_data.internal_edges);
    println!("OFFSET: {:?}", routing_data.internal_offset);

    assert_eq!(routing_data.internal_offset, vec![0, 2, 2, 4, 5]);
}

fn first_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();

    let mut invalid_values = HashSet::new();
    init_filter_list(&mut invalid_values);

    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Way(way) => {
                if filter_way(&way, &invalid_values) {
                    for node in way.nodes {
                        parse_result.nodes_used.insert(node);
                    }
                    parse_result.filtered_ways.insert(way.id);
                }
            }
            _ => {}
        }
    }
}

fn second_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();
    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Node(node) => {
                if parse_result.nodes_used.remove(&node.id) {
                    parse_result.nodes.insert(node.id, Position { lat: node.lat, lon: node.lon });
                }
            }
            _ => {}
        }
    }
}

fn third_parse(filename: &OsString, parse_result: &mut ParseData) {
    let pbf_file = File::open(&Path::new(filename)).unwrap();
    let mut pbf = OsmPbfReader::new(pbf_file);

    for obj in pbf.iter() {
        match obj {
            OsmObj::Way(way) => {
                if parse_result.filtered_ways.remove(&way.id) {
                    for node_pair in way.nodes.windows(2) {
                        if let (Some(from), Some(to)) = (node_pair.first(), node_pair.last()) {
                            if let (Some(from_node), Some(to_node)) = (parse_result.nodes.get(from), parse_result.nodes.get(to)) {
                                let edge_length = calculate_distance(from_node, to_node);
                                let edge = ParsedEdge { id_from: from.clone(), id_to: to.clone(), length: edge_length.clone() };
                                parse_result.edges.push(edge);

                                if let Some(val) = way.tags.get("oneway") {
                                    if val == "yes" {
                                        let edge_reverse = ParsedEdge { id_from: to.clone(), id_to: from.clone(), length: edge_length.clone() };
                                        parse_result.edges.push(edge_reverse);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
}

fn build_routing_data(mut parse_result: ParseData) -> RoutingData {
    let mut routing_data = RoutingData { osm_nodes: HashMap::new(), internal_nodes: Vec::new(), internal_edges: Vec::new(), internal_offset: vec![usize::max_value(); parse_result.nodes.len()] };

    parse_result.edges.sort_by(|a, b| b.id_from.cmp(&a.id_from));

    for node in parse_result.nodes.keys() {
        routing_data.internal_nodes.push(node.clone());
    }

    routing_data.internal_nodes.sort();

    for (i, node) in routing_data.internal_nodes.iter().enumerate() {
        if let Some(pos) = parse_result.nodes.remove(node) {
            routing_data.osm_nodes.insert(node.clone(), OsmNode { position: pos, internal_id: i });

            if let Some(edge) = parse_result.edges.last() {
                if edge.id_from == *node {
                    routing_data.internal_offset[i] = routing_data.internal_edges.len();
                }
            }

            loop {
                if let Some(edge) = parse_result.edges.last() {
                    if edge.id_from != *node {
                        break;
                    }
                } else {
                    break;
                }
                if let Some(edge) = parse_result.edges.pop() {
                    routing_data.internal_edges.push(RoutingEdge { target: edge.id_to as usize, length: edge.length, constraints: 0b00000001 });
                } else {
                    break;
                }
            }
        }
    }

    let mut current_offset = routing_data.internal_edges.len() - 1;

    for offset in &mut routing_data.internal_offset.iter_mut().rev() {
        if *offset == usize::max_value() {
            *offset = current_offset;
        } else {
            current_offset = *offset;
        }
    }

    return routing_data;
}

fn calculate_distance(node1: &Position, node2: &Position) -> f64 {
    let lat1 = node1.lat;
    let lat2 = node2.lat;
    let lng1 = node1.lon;
    let lng2 = node2.lon;

    let earth_radius: f64 = 6371000.0; //meters
    let d_lat = (lat2 - lat1).to_radians();
    let d_lng = (lng2 - lng1).to_radians();
    let a = (d_lat / 2.0).sin() * (d_lat / 2.0).sin() + lat1.to_radians().cos() * lat2.to_radians().cos() * (d_lng / 2.0).sin() * (d_lng / 2.0).sin();
    let c = 2.0 * a.sqrt().atan2((1.0 - a).sqrt());
    let dist = earth_radius * c;

    return dist;
}

fn init_filter_list(invalid_values: &mut HashSet<&str>) {
    invalid_values.insert("services");
    invalid_values.insert("pedestrian");
    invalid_values.insert("raceway");
    invalid_values.insert("footway");
    invalid_values.insert("path");
    invalid_values.insert("steps");
    invalid_values.insert("bridleway");
    invalid_values.insert("construction");
}

fn filter_way(way: &osmpbfreader::Way, invalid_values: &HashSet<&str>) -> bool {
    if let Some(value) = way.tags.get("highway") {
        return !invalid_values.contains(&value.as_str());
    } else {
        return false;
    }
}<|endoftext|>


<commit_before>//! Muxed. A tmux project manager with no runtime dependencies.
extern crate common;
extern crate docopt;
extern crate edit;
extern crate load;
extern crate new;
extern crate snapshot;

use common::args::Args;
use docopt::Docopt;
use std::env;
use std::process::exit;

#[macro_export]
macro_rules! try_or_err (
    ($expr: expr) => ({
        match $expr {
            Ok(val) => val,
            Err(e) => {
                println!("Muxed ran in to a problem: {}", e);
                exit(1);
            }
        }
    })
);

static DISALLOWED_SHORTHAND_PROJECT_NAMES: [&str; 4] = ["new", "edit", "load", "snapshot"];

static USAGE: &str = "
Usage:
    muxed [options] <project>
    muxed edit [options] <project>
    muxed new [options] <project>
    muxed snapshot [options] <project>
    muxed load [options] <project>
    muxed (-h | --help)
    muxed (-v | --version)

Flags:
    -d                  If you want to create a muxed session without connecting to it
    -f                  Overwrite existing file if one exists
    --debug             Prints debug information while executing (project opening only)
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -p <project_dir>    The directory your project config files live in. Defaults to ~/.muxed/
    -t <session>        The name of the running TMUX session to codify

Args:
    <project>           The name of your project to open

Subcommands:
    edit <project>                   Edit an existing project file
    new <project>                    To create a new project file
    snapshot -t <session> <project>  Capture a running session and create a config file for it
    load                             Load the specified project, this is the default command
";

/// The main execution method.
/// Currently accepts a single option. The option represents a configuration
/// file in the same naming format. Given a project file name `projectName.yml`
/// in the `~/.muxed/` directory.
///
/// # Examples
///
/// ~/.muxed/projectName.yml
///
/// ```
/// root: ~/projects/muxed/
/// windows:
///     - cargo: "cargo build"
///     - vim: "vim ."
///     - git: ""
/// ```
///
/// You can run the command:
///
/// ```
/// $ ./muxed projectName
/// ```
pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if args.flag_v {
        println!("Muxed {}", env!("CARGO_PKG_VERSION"));
        exit(0);
    };

    if args.cmd_new {
        try_or_err!(new::exec(args))
    } else if args.cmd_edit {
        try_or_err!(edit::exec(args))
    } else if args.cmd_snapshot {
        try_or_err!(snapshot::exec(args))
    } else if args.cmd_load {
        try_or_err!(load::exec(args))
    } else {
        if DISALLOWED_SHORTHAND_PROJECT_NAMES.contains(&args.arg_project.as_ref()) {
            println!(
                "Tried to call sub-command '{}' without a project. Please specify a project name.",
                args.arg_project
            );
            exit(1);
        }

        try_or_err!(load::exec(args))
    }
}
<commit_msg>Not returning anything here so add some semis<commit_after>//! Muxed. A tmux project manager with no runtime dependencies.
extern crate common;
extern crate docopt;
extern crate edit;
extern crate load;
extern crate new;
extern crate snapshot;

use common::args::Args;
use docopt::Docopt;
use std::env;
use std::process::exit;

#[macro_export]
macro_rules! try_or_err (
    ($expr: expr) => ({
        match $expr {
            Ok(val) => val,
            Err(e) => {
                println!("Muxed ran in to a problem: {}", e);
                exit(1);
            }
        }
    })
);

static DISALLOWED_SHORTHAND_PROJECT_NAMES: [&str; 4] = ["new", "edit", "load", "snapshot"];

static USAGE: &str = "
Usage:
    muxed [options] <project>
    muxed edit [options] <project>
    muxed new [options] <project>
    muxed snapshot [options] <project>
    muxed load [options] <project>
    muxed (-h | --help)
    muxed (-v | --version)

Flags:
    -d                  If you want to create a muxed session without connecting to it
    -f                  Overwrite existing file if one exists
    --debug             Prints debug information while executing (project opening only)
    -h, --help          Prints help information
    -v, --version       Prints version information

Options:
    -p <project_dir>    The directory your project config files live in. Defaults to ~/.muxed/
    -t <session>        The name of the running TMUX session to codify

Args:
    <project>           The name of your project to open

Subcommands:
    edit <project>                   Edit an existing project file
    new <project>                    To create a new project file
    snapshot -t <session> <project>  Capture a running session and create a config file for it
    load                             Load the specified project, this is the default command
";

/// The main execution method.
/// Currently accepts a single option. The option represents a configuration
/// file in the same naming format. Given a project file name `projectName.yml`
/// in the `~/.muxed/` directory.
///
/// # Examples
///
/// ~/.muxed/projectName.yml
///
/// ```
/// root: ~/projects/muxed/
/// windows:
///     - cargo: "cargo build"
///     - vim: "vim ."
///     - git: ""
/// ```
///
/// You can run the command:
///
/// ```
/// $ ./muxed projectName
/// ```
pub fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.deserialize())
        .unwrap_or_else(|e| e.exit());

    if args.flag_v {
        println!("Muxed {}", env!("CARGO_PKG_VERSION"));
        exit(0);
    };

    if args.cmd_new {
        try_or_err!(new::exec(args));
    } else if args.cmd_edit {
        try_or_err!(edit::exec(args));
    } else if args.cmd_snapshot {
        try_or_err!(snapshot::exec(args));
    } else if args.cmd_load {
        try_or_err!(load::exec(args));
    } else {
        if DISALLOWED_SHORTHAND_PROJECT_NAMES.contains(&args.arg_project.as_ref()) {
            println!(
                "Tried to call sub-command '{}' without a project. Please specify a project name.",
                args.arg_project
            );
            exit(1);
        }

        try_or_err!(load::exec(args));
    }
}
<|endoftext|>


<commit_before>extern crate unicode_names;

use std::env;
use std::fmt;
use std::char;

struct Describable {
    c: char,
}

impl fmt::Display for Describable {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match unicode_names::name(self.c) {
            // TODO: restructure this for high-bit chars
            Some(n) => {
                let num = self.c as u32;
                let quote : String = self.c.escape_default().collect();
                write!(f, "{} = {}, {:}, 0x{:x}, 0{:o}, bits {:b}: prints as {}",
                       self.c, n, num, num, num, num, quote)
            },
            None => write!(f, "{} is unknown", self.c)
        }
    }
}

/// Takes a stringly description of a character (the character itself,
/// or a unicode code point name) and returns either None (if the
/// character description is not understood), or Some(Describable)
/// that holds the character.
fn from_arg(spec: &str) -> Vec<Describable> {
    let mut res : Vec<Describable> = Vec::new();

    if spec.chars().count() == 1 {
        spec.chars().next().map(|c| res.push(Describable{c: c}));
    } else {
        unicode_names::character(spec).map(|c| res.push(Describable{c: c}));
    }
    for base in vec![16, 10, 8, 2] {
        let _ = u32::from_str_radix(spec, base.clone()).ok().
            map(|num| char::from_u32(num).map(|c| res.push(Describable{c: c})));
    }
    res
}

fn main() {
    let args =
        env::args().skip(1)
        .flat_map(|argument| from_arg(argument.as_ref()));
    for c in args {
        println!("{}", c);
    }
}

#[test]
fn from_arg_translates_chars() {
    assert_eq!('c', from_arg("c")[0].c);
    assert_eq!(']', from_arg("]")[0].c);
}

#[test]
fn from_arg_translates_descriptions() {
    assert_eq!('c', from_arg("latin small letter c")[0].c);
    assert_eq!(']', from_arg("right square bracket")[0].c);
}

#[test]
fn from_arg_translates_numbers() {
    let received = from_arg("60");
    let mut iter = received.iter();
    assert_eq!('`', iter.next().unwrap().c);
    assert_eq!('<', iter.next().unwrap().c);
    assert_eq!('0', iter.next().unwrap().c);
}
<commit_msg>Fix printing of control chars<commit_after>extern crate unicode_names;

use std::env;
use std::fmt;
use std::char;

struct Describable {
    c: char,
}

impl fmt::Display for Describable {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        let cp : Codepoint = self.c.into();
        try!(cp.fmt(f));
        let quote : String = self.c.escape_default().collect();
        try!(write!(f, ": prints as {}", quote));
        match unicode_names::name(self.c) {
            Some(n) => {
                write!(f, "\nUnicode name: {} = {}\n",
                       self.c, n)
            },
            None => write!(f, "\n")
        }
    }
}

impl std::convert::From<char> for Describable {
    fn from(c: char) -> Describable {
        Describable{c: c}
    }
}

enum Codepoint {
    ASCII7bit(char),
    Latin1(char),
    Unicode(char)
}

impl std::convert::From<char> for Codepoint {
    fn from(c: char) -> Codepoint {
        match c as u32 {
            0 ... 128 => Codepoint::ASCII7bit(c),
            128 ... 256 => Codepoint::Latin1(c),
            _ => Codepoint::Unicode(c),
        }
    }
}

impl fmt::Display for Codepoint {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &Codepoint::ASCII7bit(c) => {
                let num = c as u32;
                write!(f, "ASCII  {:02x}, {:3}, 0x{:02x}, 0{:03o}, bits {:08b}",
                       num, num, num, num, num)
            }
            &Codepoint::Latin1(c) => {
                let num = c as u32;
                write!(f, "LATIN1 {:x}, {:}, 0x{:x}, 0{:o}, bits {:b}",
                       num, num, num, num, num)
            }
            &Codepoint::Unicode(c) => {
                let num = c as u32;
                write!(f, "UCS 4  {:x}, {:}, 0x{:x}, 0{:o}, bits {:b}",
                       num, num, num, num, num)
            }
        }
    }
}

/// Takes a stringly description of a character (the character itself,
/// or a unicode code point name) and returns either None (if the
/// character description is not understood), or Some(Describable)
/// that holds the character.
fn from_arg(spec: &str) -> Vec<Describable> {
    let mut res : Vec<Describable> = Vec::new();

    if spec.chars().count() == 1 {
        spec.chars().next().map(|c| res.push(Describable{c: c}));
    } else {
        unicode_names::character(spec).map(|c| res.push(Describable{c: c}));
    }
    for base in vec![16, 10, 8, 2] {
        let _ = u32::from_str_radix(spec, base.clone()).ok().
            map(|num| char::from_u32(num).map(|c| res.push(Describable{c: c})));
    }
    res
}

fn main() {
    let args =
        env::args().skip(1)
        .flat_map(|argument| from_arg(argument.as_ref()));
    for c in args {
        println!("{}", c);
    }
}

#[test]
fn from_arg_translates_chars() {
    assert_eq!('c', from_arg("c")[0].c);
    assert_eq!(']', from_arg("]")[0].c);
}

#[test]
fn from_arg_translates_descriptions() {
    assert_eq!('c', from_arg("latin small letter c")[0].c);
    assert_eq!(']', from_arg("right square bracket")[0].c);
}

#[test]
fn from_arg_translates_numbers() {
    let received = from_arg("60");
    let mut iter = received.iter();
    assert_eq!('`', iter.next().unwrap().c);
    assert_eq!('<', iter.next().unwrap().c);
    assert_eq!('0', iter.next().unwrap().c);
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> (Option<String>, Option<String>) {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            (Some(user), Some(repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            (None, None)
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                match env::var("GH_TOKEN") {
                    Ok(token) => Some(token),
                    Err(err) => {
                        logger::warn(format!("GH_TOKEN not set: {:?}", err));
                        None
                    }
                }
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> String {
    let cargo_token = env::var("CARGO_TOKEN")
        .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));
    cargo_token
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    let (user, repo) = get_user_and_repo(&repository_path);
    if user.is_some() {
        config_builder.user(user.unwrap());
    }
    if repo.is_some() {
        config_builder.user(repo.unwrap());
    }
    let gh_token = get_github_token(&repository_path);
    if gh_token.is_some() {
        config_builder.gh_token(gh_token.unwrap());
    }

    config_builder.cargo_token(get_cargo_token());
    config_builder.repository(get_repo(&repository_path));
}
<commit_msg>refactor: build config struct<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

const COMMITTER_ERROR_MESSAGE: &'static str = r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn push_to_github(config: &config::Config, tag_name: &str) {
    logger::stdout("Pushing new commit and tag");
    git::push(&config, &tag_name)
        .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

    logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
    thread::sleep(Duration::from_secs(1));
}

fn release_on_github(config: &config::Config, tag_message: &str, tag_name: &str) {
    if github::can_release(&config) {
        logger::stdout("Creating GitHub release");
        github::release(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));
    } else {
        logger::stdout("Project not hosted on GitHub. Skipping release step");
    }
}

fn release_on_cratesio(config: &config::Config) {
    logger::stdout("Publishing crate on crates.io");
    if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
        print_exit!("Failed to publish on crates.io");
    }
}

fn generate_changelog(repository_path: &str, version: &Version, new_version: &String) -> String {
    logger::stdout(format!("New version would be: {}", new_version));
    logger::stdout("Would write the following Changelog:");
    match changelog::generate(repository_path, &version.to_string(), new_version) {
        Ok(log) => log,
        Err(err) => {
            logger::stderr(format!("Generating Changelog failed: {:?}", err));
            process::exit(1)
        }
    }
}

fn write_changelog(repository_path: &str, version: &Version, new_version: &str) {
    logger::stdout("Writing Changelog");
    changelog::write(repository_path, &version.to_string(), &new_version)
        .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));
}

fn print_changelog(changelog: &str) {
    logger::stdout("====================================");
    logger::stdout(changelog);
    logger::stdout("====================================");
    logger::stdout("Would create annotated git tag");
}

fn package_crate(config: &config::Config, repository_path: &str, new_version: &str) {
    if config.release_mode {
        logger::stdout("Updating lockfile");
        if !cargo::update_lockfile(repository_path) {
            print_exit!("`cargo fetch` failed. See above for the cargo error message.");
        }
    }

    git::commit_files(&config, &new_version)
        .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

    logger::stdout("Package crate");
    if !cargo::package(repository_path) {
        print_exit!("`cargo package` failed. See above for the cargo error message.");
    }
}

fn get_repo(repository_path: &str) -> git2::Repository {
    match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    }
}

fn get_repository_path(args: &Args) -> String {
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is
                                                            not a directory"));
    let repo_path = path.to_str().unwrap_or_else(|| print_exit!("Path is not valid unicode"));
    repo_path.to_string()
}

fn get_signature<'a>(repository_path: String) -> git2::Signature<'a> {
    let repo = get_repo(&repository_path);
    let signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(COMMITTER_ERROR_MESSAGE);
                process::exit(1);
            }
    };

    signature.to_owned()
}

fn get_user_and_repo(repository_path: &str) -> (Option<String>, Option<String>) {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            let (user, repo_name) = user_repo_from_url(&url)
                .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));

            (Some(user), Some(repo_name))
        },
        Err(err) => {
            logger::warn(format!("Could not determine the origin remote url: {:?}", err));
            logger::warn("semantic-rs can't push changes or create a release on GitHub");
            (None, None)
        }
    }
}

fn get_github_token(repository_path: &str) -> Option<String> {
    let repo = get_repo(repository_path);
    let remote_or_none = repo.find_remote("origin");
    match remote_or_none {
        Ok(remote) => {
            let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
            if github::is_github_url(&url) {
                match env::var("GH_TOKEN") {
                    Ok(token) => Some(token),
                    Err(err) => {
                        logger::warn(format!("GH_TOKEN not set: {:?}", err));
                        None
                    }
                }
            } else {
                None
            }
        },
        Err(_) => None
    }
}

fn get_cargo_token() -> String {
    let cargo_token = env::var("CARGO_TOKEN")
        .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));
    cargo_token
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");
    println!("semantic.rs 🚀");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);
    let repository_path = get_repository_path(&args);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch.clone());
    config_builder.repository_path(repository_path.clone());
    config_builder.signature(get_signature(repository_path.clone()));
    let (user, repo) = get_user_and_repo(&repository_path);
    if user.is_some() {
        config_builder.user(user.unwrap());
    }
    if repo.is_some() {
        config_builder.user(repo.unwrap());
    }
    let gh_token = get_github_token(&repository_path);
    if gh_token.is_some() {
        config_builder.gh_token(gh_token.unwrap());
    }

    config_builder.cargo_token(get_cargo_token());
    config_builder.repository(get_repo(&repository_path));
    let config = config_builder.build();
}
<|endoftext|>