<commit_before>mod utils;

use self::utils::*;

use ::{ROUTE_ATTR, ROUTE_INFO_ATTR};

use std::mem::transmute;
use std::collections::HashMap;

use rustc::lint::{Level, LateContext, LintContext, LintPass, LateLintPass, LintArray};
use rustc::hir::{Item, Expr, Crate, Decl, FnDecl, Body, QPath, PatKind};
use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::ty::Ty;
use rustc::hir::intravisit::{FnKind};
use rustc::hir::Ty_::*;
use rustc::hir::Decl_::*;
use rustc::hir::Expr_::*;

use syntax_pos::Span;
use syntax::symbol::Symbol as Name;
use syntax::ast::NodeId;

const STATE_TYPE: &'static [&'static str] = &["rocket", "request", "state", "State"];

// Information about a specific Rocket instance.
#[derive(Debug, Default)]
struct InstanceInfo {
    // Mapping from mounted struct info to the span of the mounted call.
    mounted: HashMap<DefId, Span>,
    // Mapping from managed types to the span of the manage call.
    managed: HashMap<Ty<'static>, Span>,
}

/// A `Receiver` captures the "receiver" of a Rocket instance method call. A
/// Receiver can be an existing instance of Rocket or a call to an Rocket
/// initialization function.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum Receiver {
    Instance(DefId, Span),
    Call(NodeId, Span),
}

impl Receiver {
    /// Returns the span associated with the receiver.
    pub fn span(&self) -> Span {
        match *self {
            Receiver::Instance(_, sp) | Receiver::Call(_, sp) => sp
        }
    }
}

#[derive(Debug, Default)]
pub struct RocketLint {
    // All of the types that were requested as managed state.
    // (fn_name, fn_span, info_struct_def_id, req_type, req_param_span)
    requested: Vec<(Name, Span, DefId, Ty<'static>, Span)>,
    // Mapping from a `Rocket` instance initialization call span (an ignite or
    // custom call) to the collected info about that instance.
    instances: HashMap<Option<Receiver>, InstanceInfo>,
    // Map of all route info structure names found in the program to its defid.
    // This is used to map a declared route to its info structure defid.
    info_structs: HashMap<Name, DefId>,
    // The name, span, and info DefId for all route functions found. The DefId
    // is obtained by indexing into info_structs with the name found in the
    // attribute that Rocket generates.
    declared: Vec<(Name, Span, DefId)>,
    // Mapping from known named Rocket instances to initial receiver. This is
    // used to do a sort-of flow-based analysis. We track variable declarations
    // and link calls to Rocket methods to the (as best as we can tell) initial
    // call to generate that Rocket instance. We use this to group calls to
    // `manage` and `mount` to give more accurate warnings.
    instance_vars: HashMap<DefId, Receiver>,
}

declare_lint!(UNMOUNTED_ROUTE, Warn, "Warn on routes that are unmounted.");

declare_lint!(UNMANAGED_STATE, Warn, "Warn on declared use of unmanaged state.");

impl<'tcx> LintPass for RocketLint {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNMANAGED_STATE, UNMOUNTED_ROUTE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RocketLint {
    // This fills out the `instance_vars` table by tracking calls to
    // function/methods that create Rocket instances. If the call is a method
    // call with a receiver that we know is a Rocket instance, then we know it's
    // been moved, and we track that move by linking all definition to the same
    // receiver.
    fn check_decl(&mut self, cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl) {
        // We only care about local declarations...everything else seems very
        // unlikely. This is imperfect, after all.
        if let DeclLocal(ref local) = decl.node {
            // Retrieve the def_id for the new binding.
            let new_def_id = match local.pat.node {
                PatKind::Binding(_, def_id, ..) => def_id ,
                _ => return
            };

            // `init` is the RHS of the declaration.
            if let Some(ref init) = local.init {
                // We only care about declarations that result in Rocket insts.
                if !returns_rocket_instance(cx, init) {
                    return;
                }

                let (expr, span) = match find_initial_receiver(cx, init) {
                    Some(expr) => (expr, expr.span),
                    None => return
                };

                // If the receiver is a path, check if this path was declared
                // before by another binding and use that binding's receiver as
                // this binding's receiver, essentially taking us back in time.
                // If we don't know about it, just insert a new receiver.
                if let ExprPath(QPath::Resolved(_, ref path)) = expr.node {
                    if let Some(old_def_id) = path.def.def_id_opt() {
                        if let Some(&prev) = self.instance_vars.get(&old_def_id) {
                            self.instance_vars.insert(new_def_id, prev);
                        } else {
                            let recvr = Receiver::Instance(old_def_id, span);
                            self.instance_vars.insert(new_def_id, recvr);
                        }
                    }
                }

                // We use a call as a base case. Maybe it's a brand new Rocket
                // instance, maybe it's a function returning a Rocket instance.
                // Who knows. This is where imperfection comes in. We're just
                // going to assume that calls to `mount` and `manage` are
                // grouped with their originating call.
                if let ExprCall(ref expr, ..) = expr.node {
                    let recvr = Receiver::Call(expr.id, span);
                    self.instance_vars.insert(new_def_id, recvr);
                }
            }
        }
    }

    // Here, we collect all of the calls to `manage` and `mount` by instance,
    // where the instance is determined by the receiver of the call. We look up
    // the receiver in the type table we've constructed. If it's there, we use
    // it, if not, we use the call as the receiver.
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        /// Fetches the top-level `Receiver` instance given that a method call
        /// was made to the receiver `rexpr`. Top-level here means "the
        /// original". We search the `instance_vars` table to retrieve it.
        let instance_for = |lint: &mut RocketLint, rexpr: &Expr| -> Option<Receiver> {
            match rexpr.node {
                ExprPath(QPath::Resolved(_, ref p)) => {
                    p.def.def_id_opt()
                        .and_then(|id| lint.instance_vars.get(&id))
                        .map(|recvr| recvr.clone())
                }
                ExprCall(ref c, ..) => Some(Receiver::Call(c.id, rexpr.span)),
                _ => unreachable!()
            }
        };

        if let Some((recvr, args)) = rocket_method_call("manage", cx, expr) {
            let managed_val = &args[0];
            let instance = recvr.and_then(|r| instance_for(self, r));
            if let Some(managed_ty) = cx.tables.expr_ty_opt(managed_val) {
                self.instances.entry(instance)
                    .or_insert_with(|| InstanceInfo::default())
                    .managed
                    .insert(unsafe { transmute(managed_ty) }, managed_val.span);
            }
        }

        if let Some((recvr, args)) = rocket_method_call("mount", cx, expr) {
            let instance = recvr.and_then(|r| instance_for(self, r));
            for def_id in extract_mount_fn_def_ids(cx, &args[1]) {
                self.instances.entry(instance)
                    .or_insert_with(|| InstanceInfo::default())
                    .mounted
                    .insert(def_id, expr.span);
            }
        }
    }

    // We collect all of the names and defids for the info structures that
    // Rocket has generated. We do this by simply looking at the attribute,
    // which Rocket's codegen was kind enough to generate.
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        // Return early if this is not a route info structure.
        if !item.attrs.iter().any(|attr| attr.check_name(ROUTE_INFO_ATTR)) {
            return;
        }

        if let Some(def_id) = cx.tcx.hir.opt_local_def_id(item.id) {
            self.info_structs.insert(item.name, def_id);
        }
    }

    /// We do two things here: 1) we find all of the `State` request guards a
    /// user wants, and 2) we find all of the routes declared by the user. We
    /// determine that a function is a route by looking for the attribute that
    /// Rocket declared. We tie the route to the info structure, obtained from
    /// the `check_item` call, so that we can determine if the route was mounted
    /// or not. The tie is done by looking at the name of the info structure in
    /// the attribute that Rocket generated and then looking up the structure in
    /// the `info_structs` map. The structure _must_ be there since Rocket
    /// always generates the structure before the route.
    fn check_fn(&mut self,
                cx: &LateContext<'a, 'tcx>,
                kind: FnKind<'tcx>,
                decl: &'tcx FnDecl,
                _: &'tcx Body,
                fn_sp: Span,
                fn_id: NodeId) {
        // Get the name of the function, if any.
        let fn_name = match kind {
            FnKind::ItemFn(name, ..) => name,
            _ => return
        };

        // Figure out if this is a route function by trying to find the
        // `ROUTE_ATTR` attribute and extracing the info struct's name from it.
        let attr_value = kind.attrs().iter().filter_map(|attr| {
            match attr.check_name(ROUTE_ATTR) {
                false => None,
                true => attr.value.meta_item_list().and_then(|list| list[0].name())
            }
        }).next();

        // Try to get the DEF_ID using the info struct's name from the
        // `info_structs` map. Return early if anything goes awry.
        let def_id = match attr_value {
            Some(val) if self.info_structs.contains_key(&val) => {
                self.info_structs.get(&val).unwrap()
            }
            _ => return
        };

        // Add this to the list of declared routes to check for mounting later
        // unless unmounted routes were explicitly allowed for this function.
        if cx.current_level(UNMOUNTED_ROUTE) != Level::Allow {
            self.declared.push((fn_name, fn_sp, def_id.clone()));
        }

        // If unmanaged state was explicitly allowed for this function, don't
        // record any additional information. Just return now.
        if cx.current_level(UNMANAGED_STATE) == Level::Allow {
            return;
        }

        // Collect all of the `State` types into `tys`.
        let mut tys: Vec<Ty<'static>> = vec![];
        if let Some(sig) = cx.tables.liberated_fn_sigs.get(&fn_id) {
            for input_ty in sig.inputs() {
                let def_id = match input_ty.ty_to_def_id() {
                    Some(id) => id,
                    None => continue
                };

                if match_def_path(cx.tcx, def_id, STATE_TYPE) {
                    if let Some(inner_type) = input_ty.walk_shallow().next() {
                        tys.push(unsafe { transmute(inner_type) });
                    }
                }
            }
        }

        // Collect all of the spans for the `State` parameters.
        let mut spans = vec![];
        for input in decl.inputs.iter() {
            let id = input.id;
            if let TyPath(ref qpath) = input.node {
                if let Def::Struct(defid) = cx.tables.qpath_def(qpath, id) {
                    if match_def_path(cx.tcx, defid, STATE_TYPE) {
                        spans.push(input.span);
                    }
                }
            }
        }

        // Sanity check: we should have as many spans as types.
        if tys.len() != spans.len() {
            panic!("Lint error: unequal ty/span ({}/{})", tys.len(), spans.len());
        }

        // Insert the information we've collected.
        for (ty, span) in tys.into_iter().zip(spans.into_iter()) {
            self.requested.push((fn_name, fn_sp, def_id.clone(), ty, span));
        }
    }

    fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
        // Iterate through all the instances, emitting warnings.
        for (instance, info) in self.instances.iter() {
            self.unmounted_warnings(cx, *instance, info);
            self.unmanaged_warnings(cx, *instance, info);
        }
    }
}

impl RocketLint {
    fn unmounted_warnings(&self, cx: &LateContext,
                          rcvr: Option<Receiver>,
                          info: &InstanceInfo) {
        // Emit a warning for all unmounted, declared routes.
        for &(route_name, fn_sp, info_def_id) in self.declared.iter() {
            if !info.mounted.contains_key(&info_def_id) {
                let help_span = rcvr.map(|r| r.span());
                msg_and_help(cx, UNMOUNTED_ROUTE, fn_sp,
                    &format!("the '{}' route is not mounted", route_name),
                    "Rocket will not dispatch requests to unmounted routes.",
                    help_span, "maybe add a call to `mount` here?");
            }
        }
    }

    fn unmanaged_warnings(&self,
                          cx: &LateContext,
                          rcvr: Option<Receiver>,
                          info: &InstanceInfo) {
        for &(_, _, info_def_id, ty, sp) in self.requested.iter() {
            // Don't warn on unmounted routes.
            if !info.mounted.contains_key(&info_def_id) { continue }

            if !info.managed.contains_key(&ty) {
                let help_span = rcvr.map(|r| r.span());
                msg_and_help(cx, UNMANAGED_STATE, sp,
                    &format!("'{}' is not currently being managed by Rocket", ty),
                    "this 'State' request guard will always fail",
                    help_span, "maybe add a call to `manage` here?");
            }
        }
    }
}
<commit_msg>Record instances in lint from launch calls.<commit_after>mod utils;

use self::utils::*;

use ::{ROUTE_ATTR, ROUTE_INFO_ATTR};

use std::mem::transmute;
use std::collections::HashMap;

use rustc::lint::{Level, LateContext, LintContext, LintPass, LateLintPass, LintArray};
use rustc::hir::{Item, Expr, Crate, Decl, FnDecl, Body, QPath, PatKind};
use rustc::hir::def::Def;
use rustc::hir::def_id::DefId;
use rustc::ty::Ty;
use rustc::hir::intravisit::{FnKind};
use rustc::hir::Ty_::*;
use rustc::hir::Decl_::*;
use rustc::hir::Expr_::*;

use syntax_pos::Span;
use syntax::symbol::Symbol as Name;
use syntax::ast::NodeId;

const STATE_TYPE: &'static [&'static str] = &["rocket", "request", "state", "State"];

// Information about a specific Rocket instance.
#[derive(Debug, Default)]
struct InstanceInfo {
    // Mapping from mounted struct info to the span of the mounted call.
    mounted: HashMap<DefId, Span>,
    // Mapping from managed types to the span of the manage call.
    managed: HashMap<Ty<'static>, Span>,
}

/// A `Receiver` captures the "receiver" of a Rocket instance method call. A
/// Receiver can be an existing instance of Rocket or a call to an Rocket
/// initialization function.
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
enum Receiver {
    Instance(DefId, Span),
    Call(NodeId, Span),
}

impl Receiver {
    /// Returns the span associated with the receiver.
    pub fn span(&self) -> Span {
        match *self {
            Receiver::Instance(_, sp) | Receiver::Call(_, sp) => sp
        }
    }
}

#[derive(Debug, Default)]
pub struct RocketLint {
    // All of the types that were requested as managed state.
    // (fn_name, fn_span, info_struct_def_id, req_type, req_param_span)
    requested: Vec<(Name, Span, DefId, Ty<'static>, Span)>,
    // Mapping from a `Rocket` instance initialization call span (an ignite or
    // custom call) to the collected info about that instance.
    instances: HashMap<Option<Receiver>, InstanceInfo>,
    // Map of all route info structure names found in the program to its defid.
    // This is used to map a declared route to its info structure defid.
    info_structs: HashMap<Name, DefId>,
    // The name, span, and info DefId for all route functions found. The DefId
    // is obtained by indexing into info_structs with the name found in the
    // attribute that Rocket generates.
    declared: Vec<(Name, Span, DefId)>,
    // Mapping from known named Rocket instances to initial receiver. This is
    // used to do a sort-of flow-based analysis. We track variable declarations
    // and link calls to Rocket methods to the (as best as we can tell) initial
    // call to generate that Rocket instance. We use this to group calls to
    // `manage` and `mount` to give more accurate warnings.
    instance_vars: HashMap<DefId, Receiver>,
}

declare_lint!(UNMOUNTED_ROUTE, Warn, "Warn on routes that are unmounted.");

declare_lint!(UNMANAGED_STATE, Warn, "Warn on declared use of unmanaged state.");

impl<'tcx> LintPass for RocketLint {
    fn get_lints(&self) -> LintArray {
        lint_array!(UNMANAGED_STATE, UNMOUNTED_ROUTE)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RocketLint {
    // This fills out the `instance_vars` table by tracking calls to
    // function/methods that create Rocket instances. If the call is a method
    // call with a receiver that we know is a Rocket instance, then we know it's
    // been moved, and we track that move by linking all definition to the same
    // receiver.
    fn check_decl(&mut self, cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl) {
        // We only care about local declarations...everything else seems very
        // unlikely. This is imperfect, after all.
        if let DeclLocal(ref local) = decl.node {
            // Retrieve the def_id for the new binding.
            let new_def_id = match local.pat.node {
                PatKind::Binding(_, def_id, ..) => def_id ,
                _ => return
            };

            // `init` is the RHS of the declaration.
            if let Some(ref init) = local.init {
                // We only care about declarations that result in Rocket insts.
                if !returns_rocket_instance(cx, init) {
                    return;
                }

                let (expr, span) = match find_initial_receiver(cx, init) {
                    Some(expr) => (expr, expr.span),
                    None => return
                };

                // If the receiver is a path, check if this path was declared
                // before by another binding and use that binding's receiver as
                // this binding's receiver, essentially taking us back in time.
                // If we don't know about it, just insert a new receiver.
                if let ExprPath(QPath::Resolved(_, ref path)) = expr.node {
                    if let Some(old_def_id) = path.def.def_id_opt() {
                        if let Some(&prev) = self.instance_vars.get(&old_def_id) {
                            self.instance_vars.insert(new_def_id, prev);
                        } else {
                            let recvr = Receiver::Instance(old_def_id, span);
                            self.instance_vars.insert(new_def_id, recvr);
                        }
                    }
                }

                // We use a call as a base case. Maybe it's a brand new Rocket
                // instance, maybe it's a function returning a Rocket instance.
                // Who knows. This is where imperfection comes in. We're just
                // going to assume that calls to `mount` and `manage` are
                // grouped with their originating call.
                if let ExprCall(ref expr, ..) = expr.node {
                    let recvr = Receiver::Call(expr.id, span);
                    self.instance_vars.insert(new_def_id, recvr);
                }
            }
        }
    }

    // Here, we collect all of the calls to `manage` and `mount` by instance,
    // where the instance is determined by the receiver of the call. We look up
    // the receiver in the type table we've constructed. If it's there, we use
    // it, if not, we use the call as the receiver.
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        /// Fetches the top-level `Receiver` instance given that a method call
        /// was made to the receiver `rexpr`. Top-level here means "the
        /// original". We search the `instance_vars` table to retrieve it.
        let instance_for = |lint: &mut RocketLint, rexpr: &Expr| -> Option<Receiver> {
            match rexpr.node {
                ExprPath(QPath::Resolved(_, ref p)) => {
                    p.def.def_id_opt()
                        .and_then(|id| lint.instance_vars.get(&id))
                        .map(|recvr| recvr.clone())
                }
                ExprCall(ref c, ..) => Some(Receiver::Call(c.id, rexpr.span)),
                _ => unreachable!()
            }
        };

        if let Some((recvr, args)) = rocket_method_call("manage", cx, expr) {
            let managed_val = &args[0];
            let instance = recvr.and_then(|r| instance_for(self, r));
            if let Some(managed_ty) = cx.tables.expr_ty_opt(managed_val) {
                self.instances.entry(instance)
                    .or_insert_with(|| InstanceInfo::default())
                    .managed
                    .insert(unsafe { transmute(managed_ty) }, managed_val.span);
            }
        }

        if let Some((recvr, args)) = rocket_method_call("mount", cx, expr) {
            let instance = recvr.and_then(|r| instance_for(self, r));
            for def_id in extract_mount_fn_def_ids(cx, &args[1]) {
                self.instances.entry(instance)
                    .or_insert_with(|| InstanceInfo::default())
                    .mounted
                    .insert(def_id, expr.span);
            }
        }

        if let Some((recvr, _)) = rocket_method_call("launch", cx, expr) {
            let instance = recvr.and_then(|r| instance_for(self, r));
            self.instances.entry(instance)
                .or_insert_with(|| InstanceInfo::default());
        }
    }

    // We collect all of the names and defids for the info structures that
    // Rocket has generated. We do this by simply looking at the attribute,
    // which Rocket's codegen was kind enough to generate.
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
        // Return early if this is not a route info structure.
        if !item.attrs.iter().any(|attr| attr.check_name(ROUTE_INFO_ATTR)) {
            return;
        }

        if let Some(def_id) = cx.tcx.hir.opt_local_def_id(item.id) {
            self.info_structs.insert(item.name, def_id);
        }
    }

    /// We do two things here: 1) we find all of the `State` request guards a
    /// user wants, and 2) we find all of the routes declared by the user. We
    /// determine that a function is a route by looking for the attribute that
    /// Rocket declared. We tie the route to the info structure, obtained from
    /// the `check_item` call, so that we can determine if the route was mounted
    /// or not. The tie is done by looking at the name of the info structure in
    /// the attribute that Rocket generated and then looking up the structure in
    /// the `info_structs` map. The structure _must_ be there since Rocket
    /// always generates the structure before the route.
    fn check_fn(&mut self,
                cx: &LateContext<'a, 'tcx>,
                kind: FnKind<'tcx>,
                decl: &'tcx FnDecl,
                _: &'tcx Body,
                fn_sp: Span,
                fn_id: NodeId) {
        // Get the name of the function, if any.
        let fn_name = match kind {
            FnKind::ItemFn(name, ..) => name,
            _ => return
        };

        // Figure out if this is a route function by trying to find the
        // `ROUTE_ATTR` attribute and extracing the info struct's name from it.
        let attr_value = kind.attrs().iter().filter_map(|attr| {
            match attr.check_name(ROUTE_ATTR) {
                false => None,
                true => attr.value.meta_item_list().and_then(|list| list[0].name())
            }
        }).next();

        // Try to get the DEF_ID using the info struct's name from the
        // `info_structs` map. Return early if anything goes awry.
        let def_id = match attr_value {
            Some(val) if self.info_structs.contains_key(&val) => {
                self.info_structs.get(&val).unwrap()
            }
            _ => return
        };

        // Add this to the list of declared routes to check for mounting later
        // unless unmounted routes were explicitly allowed for this function.
        if cx.current_level(UNMOUNTED_ROUTE) != Level::Allow {
            self.declared.push((fn_name, fn_sp, def_id.clone()));
        }

        // If unmanaged state was explicitly allowed for this function, don't
        // record any additional information. Just return now.
        if cx.current_level(UNMANAGED_STATE) == Level::Allow {
            return;
        }

        // Collect all of the `State` types into `tys`.
        let mut tys: Vec<Ty<'static>> = vec![];
        if let Some(sig) = cx.tables.liberated_fn_sigs.get(&fn_id) {
            for input_ty in sig.inputs() {
                let def_id = match input_ty.ty_to_def_id() {
                    Some(id) => id,
                    None => continue
                };

                if match_def_path(cx.tcx, def_id, STATE_TYPE) {
                    if let Some(inner_type) = input_ty.walk_shallow().next() {
                        tys.push(unsafe { transmute(inner_type) });
                    }
                }
            }
        }

        // Collect all of the spans for the `State` parameters.
        let mut spans = vec![];
        for input in decl.inputs.iter() {
            let id = input.id;
            if let TyPath(ref qpath) = input.node {
                if let Def::Struct(defid) = cx.tables.qpath_def(qpath, id) {
                    if match_def_path(cx.tcx, defid, STATE_TYPE) {
                        spans.push(input.span);
                    }
                }
            }
        }

        // Sanity check: we should have as many spans as types.
        if tys.len() != spans.len() {
            panic!("Lint error: unequal ty/span ({}/{})", tys.len(), spans.len());
        }

        // Insert the information we've collected.
        for (ty, span) in tys.into_iter().zip(spans.into_iter()) {
            self.requested.push((fn_name, fn_sp, def_id.clone(), ty, span));
        }
    }

    fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
        // Iterate through all the instances, emitting warnings.
        for (instance, info) in self.instances.iter() {
            self.unmounted_warnings(cx, *instance, info);
            self.unmanaged_warnings(cx, *instance, info);
        }
    }
}

impl RocketLint {
    fn unmounted_warnings(&self, cx: &LateContext,
                          rcvr: Option<Receiver>,
                          info: &InstanceInfo) {
        // Emit a warning for all unmounted, declared routes.
        for &(route_name, fn_sp, info_def_id) in self.declared.iter() {
            if !info.mounted.contains_key(&info_def_id) {
                let help_span = rcvr.map(|r| r.span());
                msg_and_help(cx, UNMOUNTED_ROUTE, fn_sp,
                    &format!("the '{}' route is not mounted", route_name),
                    "Rocket will not dispatch requests to unmounted routes.",
                    help_span, "maybe add a call to `mount` here?");
            }
        }
    }

    fn unmanaged_warnings(&self,
                          cx: &LateContext,
                          rcvr: Option<Receiver>,
                          info: &InstanceInfo) {
        for &(_, _, info_def_id, ty, sp) in self.requested.iter() {
            // Don't warn on unmounted routes.
            if !info.mounted.contains_key(&info_def_id) { continue }

            if !info.managed.contains_key(&ty) {
                let help_span = rcvr.map(|r| r.span());
                msg_and_help(cx, UNMANAGED_STATE, sp,
                    &format!("'{}' is not currently being managed by Rocket", ty),
                    "this 'State' request guard will always fail",
                    help_span, "maybe add a call to `manage` here?");
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::error::Error;
use zeromq::Socket;
use zeromq::{BlockingRecv, BlockingSend};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let mut socket = zeromq::ReqSocket::new();
    socket
        .connect("tcp://127.0.0.1:5555")
        .await
        .expect("Failed to connect");
    println!("Connected to server");

    socket.send("Hello".into()).await?;
    let repl: String = socket.recv().await?.try_into()?;
    dbg!(repl);

    socket.send("NewHello".into()).await?;
    let repl: String = socket.recv().await?.try_into()?;
    dbg!(repl);
    Ok(())
}
<commit_msg>Changed example code a little bit to use it for comparison with C<commit_after>use std::convert::TryInto;
use std::error::Error;
use zeromq::Socket;
use zeromq::{BlockingRecv, BlockingSend};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let mut socket = zeromq::ReqSocket::new();
    socket
        .connect("tcp://127.0.0.1:5555")
        .await
        .expect("Failed to connect");
    println!("Connected to server");

    for _ in 0..200_000u64 {
        socket.send("Hello".into()).await?;
        let repl: String = socket.recv().await?.try_into()?;
        // dbg!(repl);
    }
    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(append)]
#![feature(arc_unique)]
#![feature(as_unsafe_cell)]
#![feature(borrow_state)]
#![feature(box_raw)]
#![feature(box_syntax)]
#![feature(core)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(drain)]
#![feature(hashmap_hasher)]
#![feature(mpsc_select)]
#![feature(nonzero)]
#![feature(owned_ascii_ext)]
#![feature(plugin)]
#![feature(rc_unique)]
#![feature(slice_chars)]
#![feature(str_utf16)]
#![feature(vec_push_all)]

#![deny(unsafe_code)]
#![allow(non_snake_case)]

#![doc="The script crate contains all matters DOM."]

#![plugin(string_cache_plugin)]
#![plugin(plugins)]

#[macro_use]
extern crate log;

#[macro_use] extern crate bitflags;
extern crate core;
extern crate devtools_traits;
extern crate cssparser;
extern crate euclid;
extern crate html5ever;
extern crate encoding;
extern crate fnv;
extern crate hyper;
extern crate js;
extern crate libc;
extern crate msg;
extern crate net_traits;
extern crate num;
extern crate png;
extern crate rustc_serialize;
extern crate time;
extern crate canvas;
extern crate canvas_traits;
extern crate profile_traits;
extern crate script_traits;
extern crate selectors;
extern crate smallvec;
extern crate util;
extern crate websocket;
#[macro_use]
extern crate style;
extern crate unicase;
extern crate url;
extern crate uuid;
extern crate string_cache;
extern crate offscreen_gl_context;
extern crate tendril;

pub mod cors;
pub mod document_loader;

#[macro_use]
pub mod dom;

pub mod parse;

pub mod layout_interface;
mod network_listener;
pub mod page;
pub mod script_task;
mod timers;
pub mod textinput;
pub mod clipboard_provider;
mod devtools;
mod horribly_inefficient_timers;
mod webdriver_handlers;

#[allow(unsafe_code)]
pub fn init() {
    unsafe {
        js::jsapi::JS_Init();
    }
}
<commit_msg>Check the result of JS_Init.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![feature(append)]
#![feature(arc_unique)]
#![feature(as_unsafe_cell)]
#![feature(borrow_state)]
#![feature(box_raw)]
#![feature(box_syntax)]
#![feature(core)]
#![feature(core_intrinsics)]
#![feature(custom_attribute)]
#![feature(custom_derive)]
#![feature(drain)]
#![feature(hashmap_hasher)]
#![feature(mpsc_select)]
#![feature(nonzero)]
#![feature(owned_ascii_ext)]
#![feature(plugin)]
#![feature(rc_unique)]
#![feature(slice_chars)]
#![feature(str_utf16)]
#![feature(vec_push_all)]

#![deny(unsafe_code)]
#![allow(non_snake_case)]

#![doc="The script crate contains all matters DOM."]

#![plugin(string_cache_plugin)]
#![plugin(plugins)]

#[macro_use]
extern crate log;

#[macro_use] extern crate bitflags;
extern crate core;
extern crate devtools_traits;
extern crate cssparser;
extern crate euclid;
extern crate html5ever;
extern crate encoding;
extern crate fnv;
extern crate hyper;
extern crate js;
extern crate libc;
extern crate msg;
extern crate net_traits;
extern crate num;
extern crate png;
extern crate rustc_serialize;
extern crate time;
extern crate canvas;
extern crate canvas_traits;
extern crate profile_traits;
extern crate script_traits;
extern crate selectors;
extern crate smallvec;
extern crate util;
extern crate websocket;
#[macro_use]
extern crate style;
extern crate unicase;
extern crate url;
extern crate uuid;
extern crate string_cache;
extern crate offscreen_gl_context;
extern crate tendril;

pub mod cors;
pub mod document_loader;

#[macro_use]
pub mod dom;

pub mod parse;

pub mod layout_interface;
mod network_listener;
pub mod page;
pub mod script_task;
mod timers;
pub mod textinput;
pub mod clipboard_provider;
mod devtools;
mod horribly_inefficient_timers;
mod webdriver_handlers;

#[allow(unsafe_code)]
pub fn init() {
    unsafe {
        assert_eq!(js::jsapi::JS_Init(), 1);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The `servo` test application.
//!
//! Creates a `Browser` instance with a simple implementation of
//! the compositor's `WindowMethods` to create a working web browser.
//!
//! This browser's implementation of `WindowMethods` is built on top
//! of [glutin], the cross-platform OpenGL utility and windowing
//! library.
//!
//! For the engine itself look next door in lib.rs.
//!
//! [glutin]: https://github.com/tomaka/glutin

#![feature(start)]

#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;
// The window backed by glutin
extern crate glutin_app as app;
extern crate env_logger;
#[cfg(target_os = "android")]
extern crate libc;
#[macro_use]
extern crate log;
// The Servo engine
extern crate servo;
extern crate time;

extern crate gleam;
extern crate offscreen_gl_context;

use gleam::gl;
use offscreen_gl_context::GLContext;
use servo::Browser;
use servo::compositing::windowing::WindowEvent;
use servo::net_traits::hosts;
use servo::util::opts;
use std::rc::Rc;

#[cfg(not(target_os = "android"))]
fn load_gl_when_headless() {
    gl::load_with(|addr| GLContext::get_proc_address(addr) as *const _);
}

#[cfg(target_os = "android")]
fn load_gl_when_headless() {}

fn main() {
    env_logger::init().unwrap();

    // Parse the command line options and store them globally
    opts::from_cmdline_args(&*args());

    setup_logging();

    // Possibly interpret the `HOST_FILE` environment variable
    hosts::global_init();

    let window = if opts::get().headless {
        // Load gl functions even when in headless mode,
        // to avoid crashing with webgl
        load_gl_when_headless();
        None
    } else {
        Some(app::create_window(None))
    };

    // Our wrapper around `Browser` that also implements some
    // callbacks required by the glutin window implementation.
    let mut browser = BrowserWrapper {
        browser: Browser::new(window.clone()),
    };

    maybe_register_glutin_resize_handler(&window, &mut browser);

    browser.browser.handle_events(vec![WindowEvent::InitializeCompositing]);

    // Feed events from the window to the browser until the browser
    // says to stop.
    loop {
        let should_continue = match window {
            None => browser.browser.handle_events(Vec::new()),
            Some(ref window) => browser.browser.handle_events(window.wait_events()),
        };
        if !should_continue {
            break
        }
    };

    maybe_unregister_glutin_resize_handler(&window);
}

fn maybe_register_glutin_resize_handler(window: &Option<Rc<app::window::Window>>,
                                        browser: &mut BrowserWrapper) {
    match *window {
        None => {}
        Some(ref window) => {
            unsafe {
                window.set_nested_event_loop_listener(browser);
            }
        }
    }
}

fn maybe_unregister_glutin_resize_handler(window: &Option<Rc<app::window::Window>>) {
    match *window {
        None => {}
        Some(ref window) => {
            unsafe {
                window.remove_nested_event_loop_listener();
            }
        }
    }
}

struct BrowserWrapper {
    browser: Browser,
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_events(vec![event]) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

#[cfg(target_os = "android")]
fn setup_logging() {
    android::setup_logging();
}

#[cfg(not(target_os = "android"))]
fn setup_logging() {
}

#[cfg(target_os = "android")]
/// Attempt to read parameters from a file since they are not passed to us in Android environments.
/// The first line should be the "servo" argument and the last should be the URL to load.
/// Blank lines and those beginning with a '#' are ignored.
/// Each line should be a separate parameter as would be parsed by the shell.
/// For example, "servo -p 10 http://en.wikipedia.org/wiki/Rust" would take 4 lines.
fn args() -> Vec<String> {
    use std::error::Error;
    use std::fs::File;
    use std::io::{BufRead, BufReader};

    const PARAMS_FILE: &'static str = "/sdcard/servo/android_params";
    match File::open(PARAMS_FILE) {
        Ok(f) => {
            let mut vec = Vec::new();
            let file = BufReader::new(&f);
            for line in file.lines() {
                let l = line.unwrap().trim().to_owned();
                // ignore blank lines and those that start with a '#'
                match l.is_empty() || l.as_bytes()[0] == b'#' {
                    true => (),
                    false => vec.push(l),
                }
            }
            vec
        },
        Err(e) => {
            debug!("Failed to open params file '{}': {}", PARAMS_FILE, Error::description(&e));
            vec![
                "servo".to_owned(),
                "http://en.wikipedia.org/wiki/Rust".to_owned()
            ]
        },
    }
}

#[cfg(not(target_os = "android"))]
fn args() -> Vec<String> {
    use std::env;
    env::args().collect()
}

// This extern definition ensures that the linker will not discard
// the static native lib bits, which are brought in from the NDK libraries
// we link in from build.rs.
#[cfg(target_os = "android")]
extern {
    fn app_dummy() -> libc::c_void;
}

// This macro must be used at toplevel because it defines a nested
// module, but macros can only accept identifiers - not paths -
// preventing the expansion of this macro within the android module
// without use of an additionl stub method or other hackery.
#[cfg(target_os = "android")]
android_start!(main);

#[cfg(target_os = "android")]
mod android {
    extern crate libc;
    extern crate android_glue;

    use self::libc::c_int;
    use std::borrow::ToOwned;

    pub fn setup_logging() {
        use self::libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
        //use std::env;

        //env::set_var("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
        redirect_output(STDERR_FILENO);
        redirect_output(STDOUT_FILENO);

        unsafe { super::app_dummy(); }
    }

    struct FilePtr(*mut self::libc::types::common::c95::FILE);

    unsafe impl Send for FilePtr {}

    fn redirect_output(file_no: c_int) {
        use self::libc::funcs::c95::stdio::fgets;
        use self::libc::funcs::posix88::stdio::fdopen;
        use self::libc::funcs::posix88::unistd::{pipe, dup2};
        use servo::util::task::spawn_named;
        use std::ffi::CStr;
        use std::ffi::CString;
        use std::str::from_utf8;

        unsafe {
            let mut pipes: [c_int; 2] = [ 0, 0 ];
            pipe(pipes.as_mut_ptr());
            dup2(pipes[1], file_no);
            let mode = CString::new("r").unwrap();
            let input_file = FilePtr(fdopen(pipes[0], mode.as_ptr()));
            spawn_named("android-logger".to_owned(), move || {
                static READ_SIZE: usize = 1024;
                let mut read_buffer = vec![0; READ_SIZE];
                let FilePtr(input_file) = input_file;
                loop {
                    fgets(read_buffer.as_mut_ptr(), (read_buffer.len() as i32)-1, input_file);
                    let c_str = CStr::from_ptr(read_buffer.as_ptr());
                    let slice = from_utf8(c_str.to_bytes()).unwrap();
                    android_glue::write_log(slice);
                }
            });
        }
    }

}
<commit_msg>Auto merge of #8367 - jdm:investigation2, r=metajack<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! The `servo` test application.
//!
//! Creates a `Browser` instance with a simple implementation of
//! the compositor's `WindowMethods` to create a working web browser.
//!
//! This browser's implementation of `WindowMethods` is built on top
//! of [glutin], the cross-platform OpenGL utility and windowing
//! library.
//!
//! For the engine itself look next door in lib.rs.
//!
//! [glutin]: https://github.com/tomaka/glutin

#![feature(start)]

#[cfg(target_os = "android")]
#[macro_use]
extern crate android_glue;
// The window backed by glutin
extern crate glutin_app as app;
extern crate env_logger;
#[cfg(target_os = "android")]
extern crate libc;
#[macro_use]
extern crate log;
// The Servo engine
extern crate servo;
extern crate time;

extern crate gleam;
extern crate offscreen_gl_context;

use gleam::gl;
use offscreen_gl_context::GLContext;
use servo::Browser;
use servo::compositing::windowing::WindowEvent;
use servo::net_traits::hosts;
use servo::util::opts;
use std::rc::Rc;

#[cfg(not(target_os = "android"))]
fn load_gl_when_headless() {
    gl::load_with(|addr| GLContext::get_proc_address(addr) as *const _);
}

#[cfg(target_os = "android")]
fn load_gl_when_headless() {}

fn main() {
    // Parse the command line options and store them globally
    opts::from_cmdline_args(&*args());

    if opts::get().is_running_problem_test && ::std::env::var("RUST_LOG").is_err() {
        ::std::env::set_var("RUST_LOG", "compositing::constellation");
    }

    env_logger::init().unwrap();

    setup_logging();

    // Possibly interpret the `HOST_FILE` environment variable
    hosts::global_init();

    let window = if opts::get().headless {
        // Load gl functions even when in headless mode,
        // to avoid crashing with webgl
        load_gl_when_headless();
        None
    } else {
        Some(app::create_window(None))
    };

    // Our wrapper around `Browser` that also implements some
    // callbacks required by the glutin window implementation.
    let mut browser = BrowserWrapper {
        browser: Browser::new(window.clone()),
    };

    maybe_register_glutin_resize_handler(&window, &mut browser);

    browser.browser.handle_events(vec![WindowEvent::InitializeCompositing]);

    // Feed events from the window to the browser until the browser
    // says to stop.
    loop {
        let should_continue = match window {
            None => browser.browser.handle_events(Vec::new()),
            Some(ref window) => browser.browser.handle_events(window.wait_events()),
        };
        if !should_continue {
            break
        }
    };

    maybe_unregister_glutin_resize_handler(&window);
}

fn maybe_register_glutin_resize_handler(window: &Option<Rc<app::window::Window>>,
                                        browser: &mut BrowserWrapper) {
    match *window {
        None => {}
        Some(ref window) => {
            unsafe {
                window.set_nested_event_loop_listener(browser);
            }
        }
    }
}

fn maybe_unregister_glutin_resize_handler(window: &Option<Rc<app::window::Window>>) {
    match *window {
        None => {}
        Some(ref window) => {
            unsafe {
                window.remove_nested_event_loop_listener();
            }
        }
    }
}

struct BrowserWrapper {
    browser: Browser,
}

impl app::NestedEventLoopListener for BrowserWrapper {
    fn handle_event_from_nested_event_loop(&mut self, event: WindowEvent) -> bool {
        let is_resize = match event {
            WindowEvent::Resize(..) => true,
            _ => false,
        };
        if !self.browser.handle_events(vec![event]) {
            return false
        }
        if is_resize {
            self.browser.repaint_synchronously()
        }
        true
    }
}

#[cfg(target_os = "android")]
fn setup_logging() {
    android::setup_logging();
}

#[cfg(not(target_os = "android"))]
fn setup_logging() {
}

#[cfg(target_os = "android")]
/// Attempt to read parameters from a file since they are not passed to us in Android environments.
/// The first line should be the "servo" argument and the last should be the URL to load.
/// Blank lines and those beginning with a '#' are ignored.
/// Each line should be a separate parameter as would be parsed by the shell.
/// For example, "servo -p 10 http://en.wikipedia.org/wiki/Rust" would take 4 lines.
fn args() -> Vec<String> {
    use std::error::Error;
    use std::fs::File;
    use std::io::{BufRead, BufReader};

    const PARAMS_FILE: &'static str = "/sdcard/servo/android_params";
    match File::open(PARAMS_FILE) {
        Ok(f) => {
            let mut vec = Vec::new();
            let file = BufReader::new(&f);
            for line in file.lines() {
                let l = line.unwrap().trim().to_owned();
                // ignore blank lines and those that start with a '#'
                match l.is_empty() || l.as_bytes()[0] == b'#' {
                    true => (),
                    false => vec.push(l),
                }
            }
            vec
        },
        Err(e) => {
            debug!("Failed to open params file '{}': {}", PARAMS_FILE, Error::description(&e));
            vec![
                "servo".to_owned(),
                "http://en.wikipedia.org/wiki/Rust".to_owned()
            ]
        },
    }
}

#[cfg(not(target_os = "android"))]
fn args() -> Vec<String> {
    use std::env;
    env::args().collect()
}

// This extern definition ensures that the linker will not discard
// the static native lib bits, which are brought in from the NDK libraries
// we link in from build.rs.
#[cfg(target_os = "android")]
extern {
    fn app_dummy() -> libc::c_void;
}

// This macro must be used at toplevel because it defines a nested
// module, but macros can only accept identifiers - not paths -
// preventing the expansion of this macro within the android module
// without use of an additionl stub method or other hackery.
#[cfg(target_os = "android")]
android_start!(main);

#[cfg(target_os = "android")]
mod android {
    extern crate libc;
    extern crate android_glue;

    use self::libc::c_int;
    use std::borrow::ToOwned;

    pub fn setup_logging() {
        use self::libc::consts::os::posix88::{STDERR_FILENO, STDOUT_FILENO};
        //use std::env;

        //env::set_var("RUST_LOG", "servo,gfx,msg,util,layers,js,std,rt,extra");
        redirect_output(STDERR_FILENO);
        redirect_output(STDOUT_FILENO);

        unsafe { super::app_dummy(); }
    }

    struct FilePtr(*mut self::libc::types::common::c95::FILE);

    unsafe impl Send for FilePtr {}

    fn redirect_output(file_no: c_int) {
        use self::libc::funcs::c95::stdio::fgets;
        use self::libc::funcs::posix88::stdio::fdopen;
        use self::libc::funcs::posix88::unistd::{pipe, dup2};
        use servo::util::task::spawn_named;
        use std::ffi::CStr;
        use std::ffi::CString;
        use std::str::from_utf8;

        unsafe {
            let mut pipes: [c_int; 2] = [ 0, 0 ];
            pipe(pipes.as_mut_ptr());
            dup2(pipes[1], file_no);
            let mode = CString::new("r").unwrap();
            let input_file = FilePtr(fdopen(pipes[0], mode.as_ptr()));
            spawn_named("android-logger".to_owned(), move || {
                static READ_SIZE: usize = 1024;
                let mut read_buffer = vec![0; READ_SIZE];
                let FilePtr(input_file) = input_file;
                loop {
                    fgets(read_buffer.as_mut_ptr(), (read_buffer.len() as i32)-1, input_file);
                    let c_str = CStr::from_ptr(read_buffer.as_ptr());
                    let slice = from_utf8(c_str.to_bytes()).unwrap();
                    android_glue::write_log(slice);
                }
            });
        }
    }

}
<|endoftext|>


<commit_before>extern crate nalgebra;

use memoized_flat_time_steward as s;
use {TimeSteward, DeterministicRandomId, Column, ColumnId, RowId, PredictorId, Mutator,
     TimeStewardStaticMethods, Accessor, MomentaryAccessor, PredictorAccessor};
use collision_detection::inefficient as collisions;

use time_functions::QuadraticTrajectory;
use std::rc::Rc;
use rand::Rng;
use nalgebra::Vector2;
use std::thread::sleep;
use std::time::{Instant, Duration};
use glium;
use glium::{DisplayBuild, Surface};

use std::io::Write;
macro_rules! printlnerr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

type Time = i64;
type SpaceCoordinate = i64;

const HOW_MANY_CIRCLES: i32 = 20;
const ARENA_SIZE: SpaceCoordinate = (1 << 20);
const TIME_SHIFT: u32 = 20;
const SECOND: Time = (1 << TIME_SHIFT);

#[derive(Clone)]
struct Basics;
impl ::Basics for Basics {
  type Time = Time;
  type Constants = ();
}
struct CollisionBasics {}
impl ::collision_detection::Basics for CollisionBasics {
  type StewardBasics = Basics;
  type DetectorId =();
  fn nearness_column_id() -> ColumnId {
    ColumnId(0x89ebc3ba9a24c286)
  }
}

type Nearness = ::collision_detection::Nearness<CollisionBasics>;

#[derive(Clone, Debug)]
struct Circle {
  position: QuadraticTrajectory,
  radius: SpaceCoordinate,
}
impl Column for Circle {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x6422505ce8c8ce8e)
  }
}
#[derive(Clone, Debug)]
struct Intersection {
  induced_acceleration: Vector2<SpaceCoordinate>,
}
impl Column for Intersection {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x9357f021339198a1)
  }
}

type Steward = s::Steward<Basics>;

fn get_circle_id(index: i32) -> RowId {
  DeterministicRandomId::new(&(0x86ccbeb2c140cc51u64, index))
}

fn collision_predictor <PA: PredictorAccessor <Basics, <s::Steward <Basics> as TimeStewardStaticMethods < Basics>>::EventFn >> (accessor: &mut PA, id: RowId) {
  let ids = Nearness::get_ids(accessor, id).0;
  let time;
  {

    let us = (accessor.data_and_last_change::<Circle>(ids[0])
                      .expect("a nearness exists for a circle that doesn't"),
              accessor.data_and_last_change::<Circle>(ids[1])
                      .expect("a nearness exists for a circle that doesn't"));

    let relationship = accessor.get::<Intersection>(id);
    time = QuadraticTrajectory::approximately_when_distance_passes((us.0).0.radius +
                                                                   (us.1).0.radius,
                                                                   if relationship.is_none() {
                                                                     -1
                                                                   } else {
                                                                     1
                                                                   },
                                                                   ((us.0).1.clone(),
                                                                    &(us.0).0.position),
                                                                   ((us.1).1.clone(),
                                                                    &(us.1).0.position));
    printlnerr!("Planning for {} At {}, {}", id, (us.0).1, (us.1).1);
    if time.is_none() && relationship.is_some() {
      printlnerr!(" fail {:?} {:?} {:?} {:?}", id, ids, us, relationship);
      panic!()
    }
  }
  if let Some(yes) = time {
    printlnerr!(" planned for {}", &yes);
    accessor.predict_at_time(&yes,
                             Rc::new(move |mutator| {
                               let new_relationship;
                               let mut new;
                               {
                                 let relationship = mutator.get::<Intersection>(id).clone();
                                 let us = (mutator.data_and_last_change::<Circle>(ids[0])
                                                  .expect("a nearness exists for a circle that \
                                                           doesn't (event)"),
                                           mutator.data_and_last_change::<Circle>(ids[1])
                                                  .expect("a nearness exists for a circle that \
                                                           doesn't (event)"));
                                 new = ((us.0).0.clone(), (us.1).0.clone());
                                 new.0.position.update_by(mutator.now() - (us.0).1);
                                 new.1.position.update_by(mutator.now() - (us.1).1);
                                 if let Some(intersection) = relationship {
                                   new.0
                                      .position
                                      .add_acceleration(-intersection.induced_acceleration);
                                   new.1
                                      .position
                                      .add_acceleration(intersection.induced_acceleration);
                                   new_relationship = None;
                                 } else {
                                   let acceleration = (new.0.position.evaluate() -
                                                       new.1.position.evaluate()) *
                                                      (ARENA_SIZE * 4 /
                                                       (new.0.radius + new.1.radius));
                                   new.0.position.add_acceleration(acceleration);
                                   new.1.position.add_acceleration(-acceleration);
                                   new_relationship = Some(Intersection {
                                     induced_acceleration: acceleration,
                                   });

                                 }
                               }
                               mutator.set::<Intersection>(id, new_relationship);
                               mutator.set::<Circle>(ids[0], Some(new.0));
                               mutator.set::<Circle>(ids[1], Some(new.1));
                             }));
  }
}

fn boundary_predictor <PA: PredictorAccessor <Basics, <s::Steward <Basics> as TimeStewardStaticMethods < Basics>>::EventFn >> (accessor: &mut PA, id: RowId) {
  let time;
  {
    let arena_center = QuadraticTrajectory::new(TIME_SHIFT,
                                                [ARENA_SIZE / 2, ARENA_SIZE / 2, 0, 0, 0, 0]);
    let me = accessor.data_and_last_change::<Circle>(id)
                     .expect("a prediction was recorded for a circle that doesn't exist");

    let relationship = accessor.get::<Intersection>(id);
    time = QuadraticTrajectory::approximately_when_distance_passes(ARENA_SIZE - me.0.radius,
                                                                   if relationship.is_some() {
                                                                     -1
                                                                   } else {
                                                                     1
                                                                   },
                                                                   (me.1.clone(),
                                                                    &(me.0.position)),
                                                                   (0, &(arena_center)));
  }
  if let Some(yes) = time {
    printlnerr!(" planned for {}", &yes);
    accessor.predict_at_time(&yes,
                             Rc::new(move |mutator| {
                               let new_relationship;
                               let mut new;
                               {
                                 let relationship = mutator.get::<Intersection>(id).clone();
                                 let me = mutator.data_and_last_change::<Circle>(id)
                                                 .expect("a an event was recorded for a circle \
                                                          that doesn't exist)");
                                 new = me.0.clone();
                                 new.position.update_by(mutator.now() - me.1);
                                 if let Some(intersection) = relationship {
                                   new.position
                                      .add_acceleration(-intersection.induced_acceleration);
                                   new_relationship = None;
                                 } else {
                                   let acceleration = -(new.position.evaluate() -
                                                        Vector2::new(ARENA_SIZE / 2,
                                                                     ARENA_SIZE / 2)) *
                                                      (ARENA_SIZE * 4 / (ARENA_SIZE - me.0.radius));
                                   new.position.add_acceleration(acceleration);
                                   new_relationship = Some(Intersection {
                                     induced_acceleration: acceleration,
                                   });

                                 }
                               }
                               mutator.set::<Intersection>(id, new_relationship);
                               mutator.set::<Circle>(id, Some(new));
                             }));
  }
}

#[derive(Copy, Clone)]

struct Vertex {
  direction: [f32; 2],
  center: [f32; 2],
  radius: f32,
}
implement_vertex!(Vertex, direction, center, radius);

pub fn testfunc() {

  let mut stew: Steward = ::TimeStewardStaticMethods::new_empty((),
                                                                vec![s::Predictor {
                                                 predictor_id: PredictorId(0x5375592f4da8682c),
                                                 column_id: Nearness::column_id(),
                                                 function: Rc::new(|accessor, id| {
                                                   collision_predictor(accessor, id)
                                                 }),
                                               },
                                               s::Predictor {
                                                 predictor_id: PredictorId(0x87d8a4a095350d30),
                                                 column_id: Circle::column_id(),
                                                 function: Rc::new(|accessor, id| {
boundary_predictor(accessor, id)
                                                 }),
                                               }
                                               ]);

  stew.insert_fiat_event(-1,
                         DeterministicRandomId::new(&0),
                         Rc::new(|mutator| {
                           for i in 0..HOW_MANY_CIRCLES {
                             let thingy = ARENA_SIZE / 20;
                             let radius = mutator.rng().gen_range(ARENA_SIZE / 30, ARENA_SIZE / 15);
                             let id = get_circle_id(i);

                             let position =
                               QuadraticTrajectory::new(TIME_SHIFT,
                                                        [mutator.rng().gen_range(0, ARENA_SIZE),
                                                         mutator.rng().gen_range(0, ARENA_SIZE),
                                                         mutator.rng().gen_range(-thingy, thingy),
                                                         mutator.rng().gen_range(-thingy, thingy),
                                                         0,
                                                         0]);
                             mutator.set::<Circle>(id,
                                                   Some(Circle {
                                                     position: position,
                                                     radius: radius,
                                                   }));
                             collisions::insert::<CollisionBasics, _>(mutator, id, ());
                           }
                         }));
  let vertex_shader_source = r#"
#version 140
in vec2 direction;
in vec2 center;
in float radius;
out vec2 direction_transfer;

void main() {
direction_transfer = direction*1.5;
gl_Position = vec4 ( center + direction*1.5*radius, 0.0, 1.0);
}

"#;

  let fragment_shader_source = r#"
#version 140
in vec2 direction_transfer;
out vec4 color;

void main() {
if (dot (direction_transfer,direction_transfer) <1.0) {
color = vec4 (1.0, 0.0, 0.0, 1.0);
} else {
color = vec4 (0.0, 0.0, 0.0, 0.0);

}
}

"#;

  let mut snapshots = Vec::new();

  if true {
    let display = glium::glutin::WindowBuilder::new()
                    .with_dimensions(600, 600)
                    .build_glium()
                    .expect("failed to create window");
    let program = glium::Program::from_source(&display,
                                              vertex_shader_source,
                                              fragment_shader_source,
                                              None)
                    .expect("glium program generation failed");
    let parameters = glium::DrawParameters {
      blend: glium::draw_parameters::Blend::alpha_blending(),
      ..Default::default()
    };
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);
    let start = Instant::now();
    loop {
      for ev in display.poll_events() {
        match ev {
          glium::glutin::Event::Closed => return,
          _ => (),
        }
      }

      let mut target = display.draw();
      target.clear_color(0.0, 0.0, 0.0, 1.0);
      let mut vertices = Vec::<Vertex>::new();

      let snapshot = stew.snapshot_before(&(((start.elapsed().as_secs() as i64 * 1000000000i64) +
                                             start.elapsed().subsec_nanos() as i64) *
                                            SECOND /
                                            1000000000i64))
                         .expect("steward failed to provide snapshot");
      for index in 0..HOW_MANY_CIRCLES {
        let (circle, time) = snapshot.data_and_last_change::<Circle>(get_circle_id(index))
                                     .expect("missing circle")
                                     .clone();
        let position = circle.position.updated_by(snapshot.now() - time).evaluate();
        let center = [position[0] as f32 / ARENA_SIZE as f32 - 0.5,
                      position[1] as f32 / ARENA_SIZE as f32 - 0.5];
        let radius = circle.radius as f32 / ARENA_SIZE as f32;
        // printlnerr!("drawing circ at {}, {}", center[0],center[1]);
        vertices.extend(&[Vertex {
                            center: center,
                            radius: radius,
                            direction: [1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [-1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [0.0, 1.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [-1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [0.0, -1.0],
                          }]);

      }
      target.draw(&glium::VertexBuffer::new(&display, &vertices)
                     .expect("failed to generate glium Vertex buffer"),
                  &indices,
                  &program,
                  &glium::uniforms::EmptyUniforms,
                  &parameters)
            .expect("failed target.draw");

      target.finish().expect("failed to finish drawing");

    }
    sleep(Duration::from_millis(10));
  }

  for increment in 1..21 {
    snapshots.push(stew.snapshot_before(&(increment * SECOND * 2)));
  }
  for snapshot in snapshots.iter_mut().map(|option| {
    option.as_mut().expect("all these snapshots should have been valid")
  }) {
    printlnerr!("snapshot for {}", snapshot.now());
    for index in 0..HOW_MANY_CIRCLES {
      // printlnerr!("{}", snapshot.get::<Circle> (get_circle_id (index)).expect("missing circle").position);
    }
  }
  // panic!("anyway")
}

#[test]
fn actuallytest() {
  testfunc();
}
<commit_msg>Try to optimize a thing for testing<commit_after>extern crate nalgebra;

use memoized_flat_time_steward as s;
use {TimeSteward, DeterministicRandomId, Column, ColumnId, RowId, PredictorId, Mutator,
     TimeStewardStaticMethods, Accessor, MomentaryAccessor, PredictorAccessor};
use collision_detection::inefficient as collisions;

use time_functions::QuadraticTrajectory;
use std::rc::Rc;
use rand::Rng;
use nalgebra::Vector2;
use std::thread::sleep;
use std::time::{Instant, Duration};
use glium;
use glium::{DisplayBuild, Surface};

use std::io::Write;
macro_rules! printlnerr(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
    } }
);

type Time = i64;
type SpaceCoordinate = i64;

const HOW_MANY_CIRCLES: i32 = 20;
const ARENA_SIZE: SpaceCoordinate = (1 << 20);
const TIME_SHIFT: u32 = 20;
const SECOND: Time = (1 << TIME_SHIFT);

#[derive(Clone)]
struct Basics;
impl ::Basics for Basics {
  type Time = Time;
  type Constants = ();
}
struct CollisionBasics {}
impl ::collision_detection::Basics for CollisionBasics {
  type StewardBasics = Basics;
  type DetectorId =();
  fn nearness_column_id() -> ColumnId {
    ColumnId(0x89ebc3ba9a24c286)
  }
}

type Nearness = ::collision_detection::Nearness<CollisionBasics>;

#[derive(Clone, Debug)]
struct Circle {
  position: QuadraticTrajectory,
  radius: SpaceCoordinate,
}
impl Column for Circle {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x6422505ce8c8ce8e)
  }
}
#[derive(Clone, Debug)]
struct Intersection {
  induced_acceleration: Vector2<SpaceCoordinate>,
}
impl Column for Intersection {
  type FieldType = Self;
  fn column_id() -> ColumnId {
    ColumnId(0x9357f021339198a1)
  }
}

type Steward = s::Steward<Basics>;

fn get_circle_id(index: i32) -> RowId {
  DeterministicRandomId::new(&(0x86ccbeb2c140cc51u64, index))
}

fn collision_predictor <PA: PredictorAccessor <Basics, <s::Steward <Basics> as TimeStewardStaticMethods < Basics>>::EventFn >> (accessor: &mut PA, id: RowId) {
  let ids = Nearness::get_ids(accessor, id).0;
  let time;
  {

    let us = (accessor.data_and_last_change::<Circle>(ids[0])
                      .expect("a nearness exists for a circle that doesn't"),
              accessor.data_and_last_change::<Circle>(ids[1])
                      .expect("a nearness exists for a circle that doesn't"));

    let relationship = accessor.get::<Intersection>(id);
    time = QuadraticTrajectory::approximately_when_distance_passes((us.0).0.radius +
                                                                   (us.1).0.radius,
                                                                   if relationship.is_none() {
                                                                     -1
                                                                   } else {
                                                                     1
                                                                   },
                                                                   ((us.0).1.clone(),
                                                                    &(us.0).0.position),
                                                                   ((us.1).1.clone(),
                                                                    &(us.1).0.position));
    printlnerr!("Planning for {} At {}, {}", id, (us.0).1, (us.1).1);
    if time.is_none() && relationship.is_some() {
      printlnerr!(" fail {:?} {:?} {:?} {:?}", id, ids, us, relationship);
      panic!()
    }
  }
  if let Some(yes) = time {
    printlnerr!(" planned for {}", &yes);
    accessor.predict_at_time(&yes,
                             Rc::new(move |mutator| {
                               let new_relationship;
                               let mut new;
                               {
                                 let relationship = mutator.get::<Intersection>(id).clone();
                                 let us = (mutator.data_and_last_change::<Circle>(ids[0])
                                                  .expect("a nearness exists for a circle that \
                                                           doesn't (event)"),
                                           mutator.data_and_last_change::<Circle>(ids[1])
                                                  .expect("a nearness exists for a circle that \
                                                           doesn't (event)"));
                                 new = ((us.0).0.clone(), (us.1).0.clone());
                                 new.0.position.update_by(mutator.now() - (us.0).1);
                                 new.1.position.update_by(mutator.now() - (us.1).1);
                                 if let Some(intersection) = relationship {
                                   new.0
                                      .position
                                      .add_acceleration(-intersection.induced_acceleration);
                                   new.1
                                      .position
                                      .add_acceleration(intersection.induced_acceleration);
                                   new_relationship = None;
                                 } else {
                                   let acceleration = (new.0.position.evaluate() -
                                                       new.1.position.evaluate()) *
                                                      (ARENA_SIZE * 4 /
                                                       (new.0.radius + new.1.radius));
                                   new.0.position.add_acceleration(acceleration);
                                   new.1.position.add_acceleration(-acceleration);
                                   new_relationship = Some(Intersection {
                                     induced_acceleration: acceleration,
                                   });

                                 }
                               }
                               mutator.set::<Intersection>(id, new_relationship);
                               mutator.set::<Circle>(ids[0], Some(new.0));
                               mutator.set::<Circle>(ids[1], Some(new.1));
                             }));
  }
}

fn boundary_predictor <PA: PredictorAccessor <Basics, <s::Steward <Basics> as TimeStewardStaticMethods < Basics>>::EventFn >> (accessor: &mut PA, id: RowId) {
  let time;
  {
    let arena_center = QuadraticTrajectory::new(TIME_SHIFT,
                                                [ARENA_SIZE / 2, ARENA_SIZE / 2, 0, 0, 0, 0]);
    let me = accessor.data_and_last_change::<Circle>(id)
                     .expect("a prediction was recorded for a circle that doesn't exist");

    let relationship = accessor.get::<Intersection>(id);
    time = QuadraticTrajectory::approximately_when_distance_passes(ARENA_SIZE - me.0.radius,
                                                                   if relationship.is_some() {
                                                                     -1
                                                                   } else {
                                                                     1
                                                                   },
                                                                   (me.1.clone(),
                                                                    &(me.0.position)),
                                                                   (0, &(arena_center)));
  }
  if let Some(yes) = time {
    printlnerr!(" planned for {}", &yes);
    accessor.predict_at_time(&yes,
                             Rc::new(move |mutator| {
                               let new_relationship;
                               let mut new;
                               {
                                 let relationship = mutator.get::<Intersection>(id).clone();
                                 let me = mutator.data_and_last_change::<Circle>(id)
                                                 .expect("a an event was recorded for a circle \
                                                          that doesn't exist)");
                                 new = me.0.clone();
                                 new.position.update_by(mutator.now() - me.1);
                                 if let Some(intersection) = relationship {
                                   new.position
                                      .add_acceleration(-intersection.induced_acceleration);
                                   new_relationship = None;
                                 } else {
                                   let acceleration = -(new.position.evaluate() -
                                                        Vector2::new(ARENA_SIZE / 2,
                                                                     ARENA_SIZE / 2)) *
                                                      (ARENA_SIZE * 4 / (ARENA_SIZE - me.0.radius));
                                   new.position.add_acceleration(acceleration);
                                   new_relationship = Some(Intersection {
                                     induced_acceleration: acceleration,
                                   });

                                 }
                               }
                               mutator.set::<Intersection>(id, new_relationship);
                               mutator.set::<Circle>(id, Some(new));
                             }));
  }
}

#[derive(Copy, Clone)]

struct Vertex {
  direction: [f32; 2],
  center: [f32; 2],
  radius: f32,
}
implement_vertex!(Vertex, direction, center, radius);

pub fn testfunc() {

  let mut stew: Steward = ::TimeStewardStaticMethods::new_empty((),
                                                                vec![s::Predictor {
                                                 predictor_id: PredictorId(0x5375592f4da8682c),
                                                 column_id: Nearness::column_id(),
                                                 function: Rc::new(|accessor, id| {
                                                   collision_predictor(accessor, id)
                                                 }),
                                               },
                                               s::Predictor {
                                                 predictor_id: PredictorId(0x87d8a4a095350d30),
                                                 column_id: Circle::column_id(),
                                                 function: Rc::new(|accessor, id| {
boundary_predictor(accessor, id)
                                                 }),
                                               }
                                               ]);

  stew.insert_fiat_event(-1,
                         DeterministicRandomId::new(&0),
                         Rc::new(|mutator| {
                           for i in 0..HOW_MANY_CIRCLES {
                             let thingy = ARENA_SIZE / 20;
                             let radius = mutator.rng().gen_range(ARENA_SIZE / 30, ARENA_SIZE / 15);
                             let id = get_circle_id(i);

                             let position =
                               QuadraticTrajectory::new(TIME_SHIFT,
                                                        [mutator.rng().gen_range(0, ARENA_SIZE),
                                                         mutator.rng().gen_range(0, ARENA_SIZE),
                                                         mutator.rng().gen_range(-thingy, thingy),
                                                         mutator.rng().gen_range(-thingy, thingy),
                                                         0,
                                                         0]);
                             mutator.set::<Circle>(id,
                                                   Some(Circle {
                                                     position: position,
                                                     radius: radius,
                                                   }));
                             collisions::insert::<CollisionBasics, _>(mutator, id, ());
                           }
                         }));
  let vertex_shader_source = r#"
#version 140
in vec2 direction;
in vec2 center;
in float radius;
out vec2 direction_transfer;

void main() {
direction_transfer = direction*1.5;
gl_Position = vec4 ( center + direction*1.5*radius, 0.0, 1.0);
}

"#;

  let fragment_shader_source = r#"
#version 140
in vec2 direction_transfer;
out vec4 color;

void main() {
if (dot (direction_transfer,direction_transfer) <1.0) {
color = vec4 (1.0, 0.0, 0.0, 1.0);
} else {
color = vec4 (0.0, 0.0, 0.0, 0.0);

}
}

"#;

  let mut snapshots = Vec::new();

  if true {
    let display = glium::glutin::WindowBuilder::new()
                    .with_dimensions(600, 600)
                    .build_glium()
                    .expect("failed to create window");
    let program = glium::Program::from_source(&display,
                                              vertex_shader_source,
                                              fragment_shader_source,
                                              None)
                    .expect("glium program generation failed");
    let parameters = glium::DrawParameters {
      blend: glium::draw_parameters::Blend::alpha_blending(),
      ..Default::default()
    };
    let indices = glium::index::NoIndices(glium::index::PrimitiveType::TrianglesList);

    //take care of the quadratic initial predictions before starting the timer
    stew.snapshot_before(&0);
    let start = Instant::now();

    loop {
      for ev in display.poll_events() {
        match ev {
          glium::glutin::Event::Closed => return,
          _ => (),
        }
      }

      let mut target = display.draw();
      target.clear_color(0.0, 0.0, 0.0, 1.0);
      let mut vertices = Vec::<Vertex>::new();

      let snapshot = stew.snapshot_before(&(((start.elapsed().as_secs() as i64 * 1000000000i64) +
                                             start.elapsed().subsec_nanos() as i64) *
                                            SECOND /
                                            1000000000i64))
                         .expect("steward failed to provide snapshot");
      for index in 0..HOW_MANY_CIRCLES {
        let (circle, time) = snapshot.data_and_last_change::<Circle>(get_circle_id(index))
                                     .expect("missing circle")
                                     .clone();
        let position = circle.position.updated_by(snapshot.now() - time).evaluate();
        let center = [position[0] as f32 / ARENA_SIZE as f32 - 0.5,
                      position[1] as f32 / ARENA_SIZE as f32 - 0.5];
        let radius = circle.radius as f32 / ARENA_SIZE as f32;
        // printlnerr!("drawing circ at {}, {}", center[0],center[1]);
        vertices.extend(&[Vertex {
                            center: center,
                            radius: radius,
                            direction: [1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [-1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [0.0, 1.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [-1.0, 0.0],
                          },
                          Vertex {
                            center: center,
                            radius: radius,
                            direction: [0.0, -1.0],
                          }]);

      }
      target.draw(&glium::VertexBuffer::new(&display, &vertices)
                     .expect("failed to generate glium Vertex buffer"),
                  &indices,
                  &program,
                  &glium::uniforms::EmptyUniforms,
                  &parameters)
            .expect("failed target.draw");

      target.finish().expect("failed to finish drawing");

    }
    sleep(Duration::from_millis(10));
  }

  for increment in 1..21 {
    snapshots.push(stew.snapshot_before(&(increment * SECOND * 2)));
  }
  for snapshot in snapshots.iter_mut().map(|option| {
    option.as_mut().expect("all these snapshots should have been valid")
  }) {
    printlnerr!("snapshot for {}", snapshot.now());
    for index in 0..HOW_MANY_CIRCLES {
      // printlnerr!("{}", snapshot.get::<Circle> (get_circle_id (index)).expect("missing circle").position);
    }
  }
  // panic!("anyway")
}

#[test]
fn actuallytest() {
  testfunc();
}
<|endoftext|>


<commit_before>use engine::Game;
use gui::screens::{self, Screen, ScreenChange};
use gui::{Console, Colors, Key};
use gui::chars;
use util::Rectangle;
use util::units::{Direction, Point, Size};

#[allow(missing_copy_implementations)]
pub struct GameScreen {
    map_frame: Rectangle,
    map_view: Point,
    info_frame: Rectangle,
    message_frame: Rectangle,
}

impl GameScreen {
    pub fn new() -> Box<Screen> {
        Box::new(
            GameScreen {
                map_frame: Rectangle::new(Point::new(16, 1), Size::new(63, 38)),
                map_view: Point::new(0, 0),
                info_frame: Rectangle::new(Point::new(1, 1), Size::new(13, 48)),
                message_frame: Rectangle::new(Point::new(16, 41), Size::new(63, 8)),
            }
        )
    }
}

impl Screen for GameScreen {

    #[allow(unused)]
    fn input(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        if let Some(key) = console.check_for_keypress() {
            match key {
                Key::Up => {
                    game.world.walk(Direction::Up);
                },
                Key::Down => {
                    game.world.walk(Direction::Down);
                },
                Key::Left => {
                    game.world.walk(Direction::Left);
                },
                Key::Right => {
                    game.world.walk(Direction::Right);
                },
                Key::Escape => {
                    return Some(ScreenChange::AddScreen(screens::PauseScreen::new()));
                },
                _ => {}
            }
        }

        None
    }

    #[allow(unused)]
    fn update(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        None
    }

    #[allow(unused)]
    fn render(&mut self, game: &mut Game, console: &mut Console) {
        self.draw_borders(game, console);
        self.draw_map(game, console);
        self.draw_player(game, console);
    }
}

impl GameScreen {

    #[allow(unused)]
    fn draw_borders(&self, game: &mut Game, console: &mut Console) {
        self.draw_box(console, self.map_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.map_frame.move_dir(Point::new(0, -1)).location(), "Map");

        self.draw_box(console, self.info_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.info_frame.move_dir(Point::new(0, -1)).location(), "Info");

        self.draw_box(console, self.message_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.message_frame.move_dir(Point::new(0, -1)).location(), "Messages");
    }

    fn draw_box(&self, console: &mut Console, rect: Rectangle) {
        console.put_plain(rect.location() + Point::new(0, 0), chars::NW);
        console.put_plain(rect.location() + Point::new(rect.width(), 0), chars::NE);
        console.put_plain(rect.location() + Point::new(0, rect.height()), chars::SW);
        console.put_plain(rect.location() + Point::new(rect.width(), rect.height()), chars::SE);

        for x in 1..rect.width() {
            console.put_plain(rect.location() + Point::new(x, 0), chars::HLINE);
            console.put_plain(rect.location() + Point::new(x, rect.height()), chars::HLINE);
        }

        for y in 1..rect.height() {
            console.put_plain(rect.location() + Point::new(0, y), chars::VLINE);
            console.put_plain(rect.location() + Point::new(rect.width(), y), chars::VLINE);
        }
    }

    #[allow(unused)]
    fn draw_map(&self, game: &mut Game, console: &mut Console) {
        let map = &(game.world.map);

        let (width, height) = (63, 38);

        for (y, line) in map.tiles[self.map_view.y as usize .. self.map_view.y as usize + height].iter().enumerate() {
            for (x, cell) in line[self.map_view.x as usize .. self.map_view.x as usize + width].iter().enumerate() {
                console.put(self.map_frame.location() + Point::new(x as i32, y as i32), ' ', Colors::white, cell.b_color());
            }
        }
    }

    #[allow(unused)]
    fn draw_player(&self, game: &mut Game, console: &mut Console) {
        let repr = game.world.player.repr;
        let pos = game.world.player.pos;

        let adjusted_pos = pos + self.map_frame.location() - self.map_view;

        if adjusted_pos.x >= self.map_frame.location().x && adjusted_pos.y >= self.map_frame.location().y {
            console.put_plain(self.map_frame.location() - self.map_view + pos, repr);
        }
    }

    fn view_can_move_right(&self, game: &Game) -> bool {
        self.map_view.x + self.map_frame.width() < game.world.map.width()
    }

    fn view_can_move_down(&self, game: &Game) -> bool {
        self.map_view.y + self.map_frame.height() < game.world.map.height()
    }

}
<commit_msg>Implement a fully scrolling map<commit_after>use engine::Game;
use gui::screens::{self, Screen, ScreenChange};
use gui::{Console, Colors, Key};
use gui::chars;
use util::Rectangle;
use util::units::{Direction, Point, Size};

#[allow(missing_copy_implementations)]
pub struct GameScreen {
    map_frame: Rectangle,
    map_view: Point,
    info_frame: Rectangle,
    message_frame: Rectangle,
}

impl GameScreen {
    pub fn new() -> Box<Screen> {
        Box::new(
            GameScreen {
                map_frame: Rectangle::new(Point::new(16, 1), Size::new(63, 38)),
                map_view: Point::new(0, 0),
                info_frame: Rectangle::new(Point::new(1, 1), Size::new(13, 48)),
                message_frame: Rectangle::new(Point::new(16, 41), Size::new(63, 8)),
            }
        )
    }
}

impl Screen for GameScreen {

    #[allow(unused)]
    fn input(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        if let Some(key) = console.check_for_keypress() {
            match key {
                Key::Up => {
                    game.world.walk(Direction::Up);
                },
                Key::Down => {
                    game.world.walk(Direction::Down);
                },
                Key::Left => {
                    game.world.walk(Direction::Left);
                },
                Key::Right => {
                    game.world.walk(Direction::Right);
                },
                Key::Escape => {
                    return Some(ScreenChange::AddScreen(screens::PauseScreen::new()));
                },
                _ => {}
            }
        }

        None
    }

    #[allow(unused)]
    fn update(&mut self, game: &mut Game, console: &mut Console) -> Option<ScreenChange> {
        None
    }

    #[allow(unused)]
    fn render(&mut self, game: &mut Game, console: &mut Console) {
        self.draw_borders(game, console);
        self.draw_map(game, console);
        self.draw_player(game, console);
    }
}

impl GameScreen {

    #[allow(unused)]
    fn draw_borders(&self, game: &mut Game, console: &mut Console) {
        self.draw_box(console, self.map_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.map_frame.move_dir(Point::new(0, -1)).location(), "Map");

        self.draw_box(console, self.info_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.info_frame.move_dir(Point::new(0, -1)).location(), "Info");

        self.draw_box(console, self.message_frame.move_dir(Point::new(-1, -1)).resize(Size::new(1, 1)));
        console.print_plain(self.message_frame.move_dir(Point::new(0, -1)).location(), "Messages");
    }

    fn draw_box(&self, console: &mut Console, rect: Rectangle) {
        console.put_plain(rect.location() + Point::new(0, 0), chars::NW);
        console.put_plain(rect.location() + Point::new(rect.width(), 0), chars::NE);
        console.put_plain(rect.location() + Point::new(0, rect.height()), chars::SW);
        console.put_plain(rect.location() + Point::new(rect.width(), rect.height()), chars::SE);

        for x in 1..rect.width() {
            console.put_plain(rect.location() + Point::new(x, 0), chars::HLINE);
            console.put_plain(rect.location() + Point::new(x, rect.height()), chars::HLINE);
        }

        for y in 1..rect.height() {
            console.put_plain(rect.location() + Point::new(0, y), chars::VLINE);
            console.put_plain(rect.location() + Point::new(rect.width(), y), chars::VLINE);
        }
    }

    #[allow(unused)]
    fn draw_map(&self, game: &mut Game, console: &mut Console) {
        let map = &(game.world.map);

        let (width, height) = (63, 38);

        for (y, line) in map.tiles[self.map_view.y as usize .. self.map_view.y as usize + height].iter().enumerate() {
            for (x, cell) in line[self.map_view.x as usize .. self.map_view.x as usize + width].iter().enumerate() {
                console.put(self.map_frame.location() + Point::new(x as i32, y as i32), ' ', Colors::white, cell.b_color());
            }
        }
    }

    #[allow(unused)]
    fn draw_player(&mut self, game: &mut Game, console: &mut Console) {
        let repr = game.world.player.repr;
        let pos = game.world.player.pos;

        let adjusted_pos = pos + self.map_frame.location() - self.map_view;

        if adjusted_pos.x >= self.map_frame.location().x + self.map_frame.width() - 10 {
            if self.view_can_move_right(game) {
                self.map_view = self.map_view.right(1);
            }
        }

        if adjusted_pos.y >= self.map_frame.location().y + self.map_frame.height() - 5 {
            if self.view_can_move_down(game) {
                self.map_view = self.map_view.down(1);
            }
        }

        if adjusted_pos.y <= self.map_frame.location().y + 5 {
            if self.view_can_move_up(game) {
                self.map_view = self.map_view.up(1);
            }
        }

        if adjusted_pos.x <= self.map_frame.location().x + 10 {
            if self.view_can_move_left(game) {
                self.map_view = self.map_view.left(1);
            }
        }

        if adjusted_pos.x >= self.map_frame.location().x && adjusted_pos.y >= self.map_frame.location().y {
            console.put_plain(self.map_frame.location() - self.map_view + pos, repr);
        }
    }

    fn view_can_move_up(&self, game: &Game) -> bool {
        self.map_view.y  > 0
    }

    fn view_can_move_down(&self, game: &Game) -> bool {
        self.map_view.y + self.map_frame.height() < game.world.map.height()
    }

    fn view_can_move_left(&self, game: &Game) -> bool {
        self.map_view.x > 0
    }

    fn view_can_move_right(&self, game: &Game) -> bool {
        self.map_view.x + self.map_frame.width() < game.world.map.width()
    }

}
<|endoftext|>