<commit_before>use calx_alg::{Deciban, clamp};
use calx_ecs::Entity;
use calx_grid::{Dir6, Prefab};
use command::CommandResult;
use components::BrainState;
use effect::{Damage, Effect};
use event::Event;
use form::Form;
use item::{MagicEffect, ItemType, Slot};
use location::Location;
use query::Query;
use rand::Rng;
use terraform::Terraform;
use terrain::Terrain;
use volume::Volume;
use world::{Ecs, Loadout};

/// World-mutating methods that are not exposed outside the crate.
pub trait Mutate: Query + Terraform + Sized {
    /// Advance world state after player input has been received.
    ///
    /// Returns CommandResult Ok(()) so can used to end result-returning methods.
    fn next_tick(&mut self) -> CommandResult;

    fn set_entity_location(&mut self, e: Entity, loc: Location);

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot);

    fn set_player(&mut self, player: Option<Entity>);

    /// Mark an entity as dead, but don't remove it from the system yet.
    fn kill_entity(&mut self, e: Entity);

    /// Remove an entity from the system.
    ///
    /// You generally do not want to call this directly. Mark the entity as dead and it will be
    /// removed at the end of the turn.
    fn remove_entity(&mut self, e: Entity);

    /// Compute field-of-view into entity's map memory.
    ///
    /// Does nothing for entities without a map memory component.
    fn do_fov(&mut self, e: Entity);

    /// Push an event to the event queue for this tick.
    fn push_event(&mut self, event: Event);

    /// Access the persistent random number generator.
    fn rng(&mut self) -> &mut ::Rng;

    /// Mutable access to ecs
    fn ecs_mut(&mut self) -> &mut Ecs;

    /// Standard deciban roll, convert to i32 and clamp out extremes.
    fn roll(&mut self) -> f32 { clamp(-20.0, 20.0, self.rng().gen::<Deciban>().0.round()) }

    /// Run AI for all autonomous mobs.
    fn ai_main(&mut self) {
        for npc in self.active_mobs().into_iter() {
            if !self.is_npc(npc) {
                continue;
            }
            if self.acts_this_frame(npc) {
                self.run_ai_for(npc)
            }
        }
    }

    /// Run AI for one non-player-controlled creature.
    fn run_ai_for(&mut self, npc: Entity) {
        use components::BrainState::*;
        let brain_state = self.brain_state(npc).expect("Running AI for non-mob");
        match brain_state {
            Asleep => {
                // No-op designator
            }
            Hunting(target) => {
                if let (Some(my_loc), Some(target_loc)) =
                    (self.location(npc), self.location(target))
                {
                    // TODO: Pathfind around obstacles
                    if let Some(move_dir) = my_loc.dir6_towards(target_loc) {
                        let dest = my_loc.jump(self, move_dir);
                        if dest == target_loc {
                            let _ = self.entity_melee(npc, move_dir);
                        } else {
                            let _ = self.entity_step(npc, move_dir);
                        }
                    }
                }
            }
            PlayerControl => {}
        }
    }

    fn notify_attacked_by(&mut self, victim: Entity, attacker: Entity) {
        // TODO: Check if victim is already in close combat and don't disengage against new target
        // if it is.
        self.designate_enemy(victim, attacker);
    }

    fn designate_enemy(&mut self, e: Entity, target: Entity) {
        // TODO: Probably want this logic to be more complex eventually.
        if self.is_npc(e) {
            if self.brain_state(e) == Some(BrainState::Asleep) {
                self.shout(e);
            }
            if let Some(brain) = self.ecs_mut().brain.get_mut(e) {
                brain.state = BrainState::Hunting(target);
            }
        }
    }

    /// Make a mob shout according to its type.
    fn shout(&mut self, e: Entity) {
        // TODO: Create noise, wake up other nearby monsters.
        use components::ShoutType;
        if let Some(shout) = self.ecs().brain.get(e).map(|b| b.shout) {
            match shout {
                ShoutType::Shout => {
                    msg!(self, "The {} shouts angrily.", self.entity_name(e));
                }
                ShoutType::Hiss => {
                    msg!(self, "The {} hisses angrily.", self.entity_name(e));
                }
                ShoutType::Buzz => {
                    msg!(self, "The {} buzzes loudly.", self.entity_name(e));
                }
                ShoutType::Roar => {
                    msg!(self, "The {} roars angrily.", self.entity_name(e));
                }
                ShoutType::Gurgle => {
                    msg!(self, "The {} gurgles loudly.", self.entity_name(e));
                }
                ShoutType::Silent => {}
            }
        }
    }

    /// Remove destroyed entities from system
    fn clean_dead(&mut self) {
        let kill_list: Vec<Entity> = self.entities()
            .filter(|&&e| !self.is_alive(e))
            .cloned()
            .collect();

        for e in kill_list {
            self.remove_entity(e);
        }
    }

    fn place_entity(&mut self, e: Entity, mut loc: Location) {
        if self.is_item(e) {
            loc = self.empty_item_drop_location(loc);
        }
        self.set_entity_location(e, loc);
        self.after_entity_moved(e);
    }

    fn after_entity_moved(&mut self, e: Entity) { self.do_fov(e); }

    fn entity_step(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        let loc = self.location(e).ok_or(())?.jump(self, dir);
        if self.can_enter(e, loc) {
            self.place_entity(e, loc);
            return Ok(());
        }

        Err(())
    }

    fn entity_melee(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        if let Some(loc) = self.location(e) {
            if let Some(target) = self.mob_at(loc.jump(self, dir)) {

                // XXX: Using power stat for damage, should this be different?
                let damage = ::attack_damage(
                    self.roll(),
                    self.stats(e).attack,
                    self.stats(e).power,
                    self.stats(target).defense,
                    self.stats(target).armor,
                );

                if damage == 0 {
                    msg!(
                        self,
                        "{} misses {}.",
                        self.entity_name(e),
                        self.entity_name(target)
                    );
                } else {
                    msg!(
                        self,
                        "{} hits {} for {}.",
                        self.entity_name(e),
                        self.entity_name(target),
                        damage
                    );
                }
                self.damage(target, damage, Damage::Physical, Some(e));
                return Ok(());
            }
        }
        Err(())
    }

    fn damage(&mut self, e: Entity, amount: i32, damage_type: Damage, source: Option<Entity>) {
        if let Some(attacker) = source {
            self.notify_attacked_by(e, attacker);
        }

        let max_hp = self.max_hp(e);

        let mut kill = false;
        if let Some(health) = self.ecs_mut().health.get_mut(e) {
            if amount > 0 {
                health.wounds += amount;

                if health.wounds > max_hp {
                    kill = true;
                }
            }
        }

        if kill {
            if let Some(loc) = self.location(e) {
                if self.player_sees(loc) {
                    // TODO: message templating
                    msg!(
                        self,
                        "The {} {}.",
                        self.entity_name(e),
                        match damage_type {
                            Damage::Physical => "is killed",
                            Damage::Fire => "is burned to ash",
                            Damage::Electricity => "is electrocuted",
                            Damage::Cold => "shatters to frozen pieces",
                        }
                    );
                }
            }
            self.kill_entity(e);
        }
    }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity;

    fn deploy_prefab(&mut self, origin: Location, prefab: &Prefab<(Terrain, Vec<String>)>) {
        for (p, &(ref terrain, _)) in prefab.iter() {
            let loc = origin + p;

            // Annihilate any existing entities in the drop zone.
            let es = self.entities_at(loc);
            for &e in &es {
                self.remove_entity(e);
            }

            self.set_terrain(loc, *terrain);
        }

        // Spawn entities after all terrain is in place so that initial FOV is good.
        for (p, &(_, ref entities)) in prefab.iter() {
            let loc = origin + p;

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.spawn_player(loc);
                } else {
                    let form = Form::named(spawn).expect(&format!("Form '{}' not found!", spawn));
                    self.spawn(&form.loadout, loc);
                }
            }
        }
    }

    /// Special method for setting the player start position.
    ///
    /// If player already exists and is placed in the world, do nothing.
    ///
    /// If player exists, but is not placed in spatial, teleport the player here.
    ///
    /// If player does not exist, create the initial player entity.
    fn spawn_player(&mut self, loc: Location) {
        if let Some(player) = self.player() {
            if self.location(player).is_none() {
                // Teleport player from limbo.
                self.place_entity(player, loc);
            }
        } else {
            // Initialize new player object.
            let form = Form::named("player").expect("Player form not found");
            let player = self.spawn(&form.loadout, loc);
            self.set_player(Some(player));
        }
    }

    fn entity_take(&mut self, e: Entity, item: Entity) -> Result<(), ()> {
        // Only mobs can take items.
        if !self.is_mob(e) {
            return Err(());
        }

        if !self.is_item(item) {
            return Err(());
        }

        // Somehow trying to pick up something we're inside of. Pls don't break the universe.
        if self.entity_contains(item, e) {
            panic!("Trying to pick up an entity you are inside of. This shouldn't happen");
        }

        if let Some(slot) = self.free_bag_slot(e) {
            self.equip_item(item, e, slot);
            if self.is_player(e) {
                msg!(self, "Picked up {}", self.entity_name(item));
            }

            Ok(())
        } else {
            // No more inventory space
            Err(())
        }
    }

    /// Cast an undirected spell
    fn cast_spell(
        &mut self,
        _origin: Location,
        effect: Entity,
        _caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::UntargetedUsable(effect) =
            self.ecs().item.get(effect).ok_or(())?.item_type
        {
            match effect {
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    /// Cast a directed spell
    fn cast_directed_spell(
        &mut self,
        origin: Location,
        dir: Dir6,
        effect: Entity,
        caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::TargetedUsable(effect) = self.ecs().item.get(effect).ok_or(())?.item_type {
            match effect {
                MagicEffect::Fireball => {
                    const FIREBALL_RANGE: u32 = 9;
                    const FIREBALL_RADIUS: u32 = 2;
                    const FIREBALL_EFFECT: Effect = Effect::Hit {
                        amount: 6,
                        damage: Damage::Fire,
                    };
                    let center = self.projected_explosion_center(origin, dir, FIREBALL_RANGE);
                    let volume = self.sphere_volume(center, FIREBALL_RADIUS);
                    self.apply_effect(&FIREBALL_EFFECT, &volume, caster);
                }
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    fn apply_effect_to_entity(&mut self, effect: &Effect, target: Entity, source: Option<Entity>) {
        use effect::Effect::*;
        match effect {
            &Heal(_amount) => {
                unimplemented!();
            }
            &Hit { amount, damage } => {
                self.damage(target, amount as i32, damage, source);
            }
            &Confuse => {
                unimplemented!();
            }
            &MagicMap => {
                unimplemented!();
            }
        }
    }

    fn apply_effect_to(&mut self, effect: &Effect, loc: Location, source: Option<Entity>) {
        if let Some(mob) = self.mob_at(loc) {
            self.apply_effect_to_entity(effect, mob, source);
        }
    }

    fn apply_effect(&mut self, effect: &Effect, volume: &Volume, source: Option<Entity>) {
        for loc in &volume.0 {
            self.apply_effect_to(effect, *loc, source);
        }
    }
}
<commit_msg>Enemies near player wake up and attack<commit_after>use calx_alg::{Deciban, clamp};
use calx_ecs::Entity;
use calx_grid::{Dir6, Prefab};
use command::CommandResult;
use components::BrainState;
use effect::{Damage, Effect};
use event::Event;
use form::Form;
use item::{MagicEffect, ItemType, Slot};
use location::Location;
use query::Query;
use rand::Rng;
use terraform::Terraform;
use terrain::Terrain;
use volume::Volume;
use world::{Ecs, Loadout};

/// World-mutating methods that are not exposed outside the crate.
pub trait Mutate: Query + Terraform + Sized {
    /// Advance world state after player input has been received.
    ///
    /// Returns CommandResult Ok(()) so can used to end result-returning methods.
    fn next_tick(&mut self) -> CommandResult;

    fn set_entity_location(&mut self, e: Entity, loc: Location);

    fn equip_item(&mut self, e: Entity, parent: Entity, slot: Slot);

    fn set_player(&mut self, player: Option<Entity>);

    /// Mark an entity as dead, but don't remove it from the system yet.
    fn kill_entity(&mut self, e: Entity);

    /// Remove an entity from the system.
    ///
    /// You generally do not want to call this directly. Mark the entity as dead and it will be
    /// removed at the end of the turn.
    fn remove_entity(&mut self, e: Entity);

    /// Compute field-of-view into entity's map memory.
    ///
    /// Does nothing for entities without a map memory component.
    fn do_fov(&mut self, e: Entity);

    /// Push an event to the event queue for this tick.
    fn push_event(&mut self, event: Event);

    /// Access the persistent random number generator.
    fn rng(&mut self) -> &mut ::Rng;

    /// Mutable access to ecs
    fn ecs_mut(&mut self) -> &mut Ecs;

    /// Standard deciban roll, convert to i32 and clamp out extremes.
    fn roll(&mut self) -> f32 { clamp(-20.0, 20.0, self.rng().gen::<Deciban>().0.round()) }

    /// Run AI for all autonomous mobs.
    fn ai_main(&mut self) {
        for npc in self.active_mobs().into_iter() {
            if !self.is_npc(npc) {
                continue;
            }
            if self.ticks_this_frame(npc) {
                self.run_ai_for(npc)
            }
        }
    }

    /// Run AI for one non-player-controlled creature.
    fn run_ai_for(&mut self, npc: Entity) {
        const WAKEUP_DISTANCE: i32 = 5;

        use components::BrainState::*;
        let brain_state = self.brain_state(npc).expect("Running AI for non-mob");
        match brain_state {
            Asleep => {
                // XXX: Only treat player mob as potential hostile.
                if let (Some(loc), Some(player), Some(player_loc)) = (self.location(npc), self.player(), self.player().map(|p| self.location(p)).unwrap_or(None)) {
                    if self.player_sees(loc) {
                        // Okay, tricky spot. Player might be seeing mob across a portal, in
                        // which case we can't do naive distance check.
                        // This could have a helper method that finds chart distance to self in
                        // player's map memory.
                        //
                        // For now, let's just go with mobs past portals not waking up to
                        // player.

                        if loc.metric_distance(player_loc) <= WAKEUP_DISTANCE {
                            self.designate_enemy(npc, player);
                        }
                    }
                }
            }
            Hunting(target) => {
                if let (Some(my_loc), Some(target_loc)) =
                    (self.location(npc), self.location(target))
                {
                    // TODO: Pathfind around obstacles
                    if let Some(move_dir) = my_loc.dir6_towards(target_loc) {
                        let dest = my_loc.jump(self, move_dir);
                        if dest == target_loc {
                            let _ = self.entity_melee(npc, move_dir);
                        } else {
                            let _ = self.entity_step(npc, move_dir);
                        }
                    }
                }
            }
            PlayerControl => {}
        }
    }

    fn notify_attacked_by(&mut self, victim: Entity, attacker: Entity) {
        // TODO: Check if victim is already in close combat and don't disengage against new target
        // if it is.
        self.designate_enemy(victim, attacker);
    }

    fn designate_enemy(&mut self, e: Entity, target: Entity) {
        // TODO: Probably want this logic to be more complex eventually.
        if self.is_npc(e) {
            if self.brain_state(e) == Some(BrainState::Asleep) {
                self.shout(e);
            }
            if let Some(brain) = self.ecs_mut().brain.get_mut(e) {
                brain.state = BrainState::Hunting(target);
            }
        }
    }

    /// Make a mob shout according to its type.
    fn shout(&mut self, e: Entity) {
        // TODO: Create noise, wake up other nearby monsters.
        use components::ShoutType;
        if let Some(shout) = self.ecs().brain.get(e).map(|b| b.shout) {
            match shout {
                ShoutType::Shout => {
                    msg!(self, "The {} shouts angrily.", self.entity_name(e));
                }
                ShoutType::Hiss => {
                    msg!(self, "The {} hisses angrily.", self.entity_name(e));
                }
                ShoutType::Buzz => {
                    msg!(self, "The {} buzzes loudly.", self.entity_name(e));
                }
                ShoutType::Roar => {
                    msg!(self, "The {} roars angrily.", self.entity_name(e));
                }
                ShoutType::Gurgle => {
                    msg!(self, "The {} gurgles loudly.", self.entity_name(e));
                }
                ShoutType::Silent => {}
            }
        }
    }

    /// Remove destroyed entities from system
    fn clean_dead(&mut self) {
        let kill_list: Vec<Entity> = self.entities()
            .filter(|&&e| !self.is_alive(e))
            .cloned()
            .collect();

        for e in kill_list {
            self.remove_entity(e);
        }
    }

    fn place_entity(&mut self, e: Entity, mut loc: Location) {
        if self.is_item(e) {
            loc = self.empty_item_drop_location(loc);
        }
        self.set_entity_location(e, loc);
        self.after_entity_moved(e);
    }

    fn after_entity_moved(&mut self, e: Entity) { self.do_fov(e); }

    fn entity_step(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        let loc = self.location(e).ok_or(())?.jump(self, dir);
        if self.can_enter(e, loc) {
            self.place_entity(e, loc);
            return Ok(());
        }

        Err(())
    }

    fn entity_melee(&mut self, e: Entity, dir: Dir6) -> Result<(), ()> {
        if let Some(loc) = self.location(e) {
            if let Some(target) = self.mob_at(loc.jump(self, dir)) {

                // XXX: Using power stat for damage, should this be different?
                let damage = ::attack_damage(
                    self.roll(),
                    self.stats(e).attack,
                    self.stats(e).power,
                    self.stats(target).defense,
                    self.stats(target).armor,
                );

                if damage == 0 {
                    msg!(
                        self,
                        "{} misses {}.",
                        self.entity_name(e),
                        self.entity_name(target)
                    );
                } else {
                    msg!(
                        self,
                        "{} hits {} for {}.",
                        self.entity_name(e),
                        self.entity_name(target),
                        damage
                    );
                }
                self.damage(target, damage, Damage::Physical, Some(e));
                return Ok(());
            }
        }
        Err(())
    }

    fn damage(&mut self, e: Entity, amount: i32, damage_type: Damage, source: Option<Entity>) {
        if let Some(attacker) = source {
            self.notify_attacked_by(e, attacker);
        }

        let max_hp = self.max_hp(e);

        let mut kill = false;
        if let Some(health) = self.ecs_mut().health.get_mut(e) {
            if amount > 0 {
                health.wounds += amount;

                if health.wounds > max_hp {
                    kill = true;
                }
            }
        }

        if kill {
            if let Some(loc) = self.location(e) {
                if self.player_sees(loc) {
                    // TODO: message templating
                    msg!(
                        self,
                        "The {} {}.",
                        self.entity_name(e),
                        match damage_type {
                            Damage::Physical => "is killed",
                            Damage::Fire => "is burned to ash",
                            Damage::Electricity => "is electrocuted",
                            Damage::Cold => "shatters to frozen pieces",
                        }
                    );
                }
            }
            self.kill_entity(e);
        }
    }

    fn spawn(&mut self, loadout: &Loadout, loc: Location) -> Entity;

    fn deploy_prefab(&mut self, origin: Location, prefab: &Prefab<(Terrain, Vec<String>)>) {
        for (p, &(ref terrain, _)) in prefab.iter() {
            let loc = origin + p;

            // Annihilate any existing entities in the drop zone.
            let es = self.entities_at(loc);
            for &e in &es {
                self.remove_entity(e);
            }

            self.set_terrain(loc, *terrain);
        }

        // Spawn entities after all terrain is in place so that initial FOV is good.
        for (p, &(_, ref entities)) in prefab.iter() {
            let loc = origin + p;

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.spawn_player(loc);
                } else {
                    let form = Form::named(spawn).expect(&format!("Form '{}' not found!", spawn));
                    self.spawn(&form.loadout, loc);
                }
            }
        }
    }

    /// Special method for setting the player start position.
    ///
    /// If player already exists and is placed in the world, do nothing.
    ///
    /// If player exists, but is not placed in spatial, teleport the player here.
    ///
    /// If player does not exist, create the initial player entity.
    fn spawn_player(&mut self, loc: Location) {
        if let Some(player) = self.player() {
            if self.location(player).is_none() {
                // Teleport player from limbo.
                self.place_entity(player, loc);
            }
        } else {
            // Initialize new player object.
            let form = Form::named("player").expect("Player form not found");
            let player = self.spawn(&form.loadout, loc);
            self.set_player(Some(player));
        }
    }

    fn entity_take(&mut self, e: Entity, item: Entity) -> Result<(), ()> {
        // Only mobs can take items.
        if !self.is_mob(e) {
            return Err(());
        }

        if !self.is_item(item) {
            return Err(());
        }

        // Somehow trying to pick up something we're inside of. Pls don't break the universe.
        if self.entity_contains(item, e) {
            panic!("Trying to pick up an entity you are inside of. This shouldn't happen");
        }

        if let Some(slot) = self.free_bag_slot(e) {
            self.equip_item(item, e, slot);
            if self.is_player(e) {
                msg!(self, "Picked up {}", self.entity_name(item));
            }

            Ok(())
        } else {
            // No more inventory space
            Err(())
        }
    }

    /// Cast an undirected spell
    fn cast_spell(
        &mut self,
        _origin: Location,
        effect: Entity,
        _caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::UntargetedUsable(effect) =
            self.ecs().item.get(effect).ok_or(())?.item_type
        {
            match effect {
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    /// Cast a directed spell
    fn cast_directed_spell(
        &mut self,
        origin: Location,
        dir: Dir6,
        effect: Entity,
        caster: Option<Entity>,
    ) -> Result<(), ()> {
        if let ItemType::TargetedUsable(effect) = self.ecs().item.get(effect).ok_or(())?.item_type {
            match effect {
                MagicEffect::Fireball => {
                    const FIREBALL_RANGE: u32 = 9;
                    const FIREBALL_RADIUS: u32 = 2;
                    const FIREBALL_EFFECT: Effect = Effect::Hit {
                        amount: 6,
                        damage: Damage::Fire,
                    };
                    let center = self.projected_explosion_center(origin, dir, FIREBALL_RANGE);
                    let volume = self.sphere_volume(center, FIREBALL_RADIUS);
                    self.apply_effect(&FIREBALL_EFFECT, &volume, caster);
                }
                _ => {
                    msg!(self, "TODO cast untargeted spell {:?}", effect);
                }
            }
            Ok(())
        } else {
            Err(())
        }
    }

    fn apply_effect_to_entity(&mut self, effect: &Effect, target: Entity, source: Option<Entity>) {
        use effect::Effect::*;
        match effect {
            &Heal(_amount) => {
                unimplemented!();
            }
            &Hit { amount, damage } => {
                self.damage(target, amount as i32, damage, source);
            }
            &Confuse => {
                unimplemented!();
            }
            &MagicMap => {
                unimplemented!();
            }
        }
    }

    fn apply_effect_to(&mut self, effect: &Effect, loc: Location, source: Option<Entity>) {
        if let Some(mob) = self.mob_at(loc) {
            self.apply_effect_to_entity(effect, mob, source);
        }
    }

    fn apply_effect(&mut self, effect: &Effect, volume: &Volume, source: Option<Entity>) {
        for loc in &volume.0 {
            self.apply_effect_to(effect, *loc, source);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Client functions, implementations of the NodeClient trait
//! specific to the FileWallet

use failure::ResultExt;
use futures::{stream, Stream};

use crate::libwallet::types::*;
use std::collections::HashMap;
use tokio::runtime::Runtime;

use crate::api;
use crate::error::{Error, ErrorKind};
use crate::libwallet;
use crate::util;
use crate::util::secp::pedersen;

#[derive(Clone)]
pub struct HTTPNodeClient {
	node_url: String,
	node_api_secret: Option<String>,
}

impl HTTPNodeClient {
	/// Create a new client that will communicate with the given grin node
	pub fn new(node_url: &str, node_api_secret: Option<String>) -> HTTPNodeClient {
		HTTPNodeClient {
			node_url: node_url.to_owned(),
			node_api_secret: node_api_secret,
		}
	}
}

impl NodeClient for HTTPNodeClient {
	fn node_url(&self) -> &str {
		&self.node_url
	}
	fn node_api_secret(&self) -> Option<String> {
		self.node_api_secret.clone()
	}

	fn set_node_url(&mut self, node_url: &str) {
		self.node_url = node_url.to_owned();
	}

	fn set_node_api_secret(&mut self, node_api_secret: Option<String>) {
		self.node_api_secret = node_api_secret;
	}

	/// Posts a transaction to a grin node
	fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> {
		let url;
		let dest = self.node_url();
		if fluff {
			url = format!("{}/v1/pool/push?fluff", dest);
		} else {
			url = format!("{}/v1/pool/push", dest);
		}
		let res = api::client::post_no_ret(url.as_str(), self.node_api_secret(), tx);
		if let Err(e) = res {
			let report = format!("Posting transaction to node: {}", e);
			error!("Post TX Error: {}", e);
			return Err(libwallet::ErrorKind::ClientCallback(report).into());
		}
		Ok(())
	}

	/// Return the chain tip from a given node
	fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
		let addr = self.node_url();
		let url = format!("{}/v1/chain", addr);
		let res = api::client::get::<api::Tip>(url.as_str(), self.node_api_secret());
		match res {
			Err(e) => {
				let report = format!("Getting chain height from node: {}", e);
				error!("Get chain height error: {}", e);
				Err(libwallet::ErrorKind::ClientCallback(report).into())
			}
			Ok(r) => Ok(r.height),
		}
	}

	/// Retrieve outputs from node
	fn get_outputs_from_node(
		&self,
		wallet_outputs: Vec<pedersen::Commitment>,
	) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
		let addr = self.node_url();
		// build the necessary query params -
		// ?id=xxx&id=yyy&id=zzz
		let query_params: Vec<String> = wallet_outputs
			.iter()
			.map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec())))
			.collect();

		// build a map of api outputs by commit so we can look them up efficiently
		let mut api_outputs: HashMap<pedersen::Commitment, (String, u64, u64)> = HashMap::new();
		let mut tasks = Vec::new();

		for query_chunk in query_params.chunks(500) {
			let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
			tasks.push(api::client::get_async::<Vec<api::Output>>(
				url.as_str(),
				self.node_api_secret(),
			));
		}

		let task = stream::futures_unordered(tasks).collect();

		let mut rt = Runtime::new().unwrap();
		let results = match rt.block_on(task) {
			Ok(outputs) => outputs,
			Err(e) => {
				let report = format!("Getting outputs by id: {}", e);
				error!("Outputs by id failed: {}", e);
				return Err(libwallet::ErrorKind::ClientCallback(report).into());
			}
		};

		for res in results {
			for out in res {
				api_outputs.insert(
					out.commit.commit(),
					(util::to_hex(out.commit.to_vec()), out.height, out.mmr_index),
				);
			}
		}
		Ok(api_outputs)
	}

	fn get_outputs_by_pmmr_index(
		&self,
		start_height: u64,
		max_outputs: u64,
	) -> Result<
		(
			u64,
			u64,
			Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
		),
		libwallet::Error,
	> {
		let addr = self.node_url();
		let query_param = format!("start_index={}&max={}", start_height, max_outputs);

		let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);

		let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)> =
			Vec::new();

		match api::client::get::<api::OutputListing>(url.as_str(), self.node_api_secret()) {
			Ok(o) => {
				for out in o.outputs {
					let is_coinbase = match out.output_type {
						api::OutputType::Coinbase => true,
						api::OutputType::Transaction => false,
					};
					api_outputs.push((
						out.commit,
						out.range_proof().unwrap(),
						is_coinbase,
						out.block_height.unwrap(),
						out.mmr_index,
					));
				}

				Ok((o.highest_index, o.last_retrieved_index, api_outputs))
			}
			Err(e) => {
				// if we got anything other than 200 back from server, bye
				error!(
					"get_outputs_by_pmmr_index: error contacting {}. Error: {}",
					addr, e
				);
				let report = format!("outputs by pmmr index: {}", e);
				Err(libwallet::ErrorKind::ClientCallback(report))?
			}
		}
	}
}

/// Call the wallet API to create a coinbase output for the given block_fees.
/// Will retry based on default "retry forever with backoff" behavior.
pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
	let url = format!("{}/v1/wallet/foreign/build_coinbase", dest);
	match single_create_coinbase(&url, &block_fees) {
		Err(e) => {
			error!(
				"Failed to get coinbase from {}. Run grin wallet listen?",
				url
			);
			error!("Underlying Error: {}", e.cause().unwrap());
			error!("Backtrace: {}", e.backtrace().unwrap());
			Err(e)?
		}
		Ok(res) => Ok(res),
	}
}

/// Makes a single request to the wallet API to create a new coinbase output.
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
	let res = api::client::post(url, None, block_fees).context(ErrorKind::GenericError(
		"Posting create coinbase".to_string(),
	))?;
	Ok(res)
}
<commit_msg>decrease chunk size in outputs by ids (#2747)<commit_after>// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Client functions, implementations of the NodeClient trait
//! specific to the FileWallet

use failure::ResultExt;
use futures::{stream, Stream};

use crate::libwallet::types::*;
use std::collections::HashMap;
use tokio::runtime::Runtime;

use crate::api;
use crate::error::{Error, ErrorKind};
use crate::libwallet;
use crate::util;
use crate::util::secp::pedersen;

#[derive(Clone)]
pub struct HTTPNodeClient {
	node_url: String,
	node_api_secret: Option<String>,
}

impl HTTPNodeClient {
	/// Create a new client that will communicate with the given grin node
	pub fn new(node_url: &str, node_api_secret: Option<String>) -> HTTPNodeClient {
		HTTPNodeClient {
			node_url: node_url.to_owned(),
			node_api_secret: node_api_secret,
		}
	}
}

impl NodeClient for HTTPNodeClient {
	fn node_url(&self) -> &str {
		&self.node_url
	}
	fn node_api_secret(&self) -> Option<String> {
		self.node_api_secret.clone()
	}

	fn set_node_url(&mut self, node_url: &str) {
		self.node_url = node_url.to_owned();
	}

	fn set_node_api_secret(&mut self, node_api_secret: Option<String>) {
		self.node_api_secret = node_api_secret;
	}

	/// Posts a transaction to a grin node
	fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> {
		let url;
		let dest = self.node_url();
		if fluff {
			url = format!("{}/v1/pool/push?fluff", dest);
		} else {
			url = format!("{}/v1/pool/push", dest);
		}
		let res = api::client::post_no_ret(url.as_str(), self.node_api_secret(), tx);
		if let Err(e) = res {
			let report = format!("Posting transaction to node: {}", e);
			error!("Post TX Error: {}", e);
			return Err(libwallet::ErrorKind::ClientCallback(report).into());
		}
		Ok(())
	}

	/// Return the chain tip from a given node
	fn get_chain_height(&self) -> Result<u64, libwallet::Error> {
		let addr = self.node_url();
		let url = format!("{}/v1/chain", addr);
		let res = api::client::get::<api::Tip>(url.as_str(), self.node_api_secret());
		match res {
			Err(e) => {
				let report = format!("Getting chain height from node: {}", e);
				error!("Get chain height error: {}", e);
				Err(libwallet::ErrorKind::ClientCallback(report).into())
			}
			Ok(r) => Ok(r.height),
		}
	}

	/// Retrieve outputs from node
	fn get_outputs_from_node(
		&self,
		wallet_outputs: Vec<pedersen::Commitment>,
	) -> Result<HashMap<pedersen::Commitment, (String, u64, u64)>, libwallet::Error> {
		let addr = self.node_url();
		// build the necessary query params -
		// ?id=xxx&id=yyy&id=zzz
		let query_params: Vec<String> = wallet_outputs
			.iter()
			.map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec())))
			.collect();

		// build a map of api outputs by commit so we can look them up efficiently
		let mut api_outputs: HashMap<pedersen::Commitment, (String, u64, u64)> = HashMap::new();
		let mut tasks = Vec::new();

		for query_chunk in query_params.chunks(200) {
			let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),);
			tasks.push(api::client::get_async::<Vec<api::Output>>(
				url.as_str(),
				self.node_api_secret(),
			));
		}

		let task = stream::futures_unordered(tasks).collect();

		let mut rt = Runtime::new().unwrap();
		let results = match rt.block_on(task) {
			Ok(outputs) => outputs,
			Err(e) => {
				let report = format!("Getting outputs by id: {}", e);
				error!("Outputs by id failed: {}", e);
				return Err(libwallet::ErrorKind::ClientCallback(report).into());
			}
		};

		for res in results {
			for out in res {
				api_outputs.insert(
					out.commit.commit(),
					(util::to_hex(out.commit.to_vec()), out.height, out.mmr_index),
				);
			}
		}
		Ok(api_outputs)
	}

	fn get_outputs_by_pmmr_index(
		&self,
		start_height: u64,
		max_outputs: u64,
	) -> Result<
		(
			u64,
			u64,
			Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)>,
		),
		libwallet::Error,
	> {
		let addr = self.node_url();
		let query_param = format!("start_index={}&max={}", start_height, max_outputs);

		let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,);

		let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)> =
			Vec::new();

		match api::client::get::<api::OutputListing>(url.as_str(), self.node_api_secret()) {
			Ok(o) => {
				for out in o.outputs {
					let is_coinbase = match out.output_type {
						api::OutputType::Coinbase => true,
						api::OutputType::Transaction => false,
					};
					api_outputs.push((
						out.commit,
						out.range_proof().unwrap(),
						is_coinbase,
						out.block_height.unwrap(),
						out.mmr_index,
					));
				}

				Ok((o.highest_index, o.last_retrieved_index, api_outputs))
			}
			Err(e) => {
				// if we got anything other than 200 back from server, bye
				error!(
					"get_outputs_by_pmmr_index: error contacting {}. Error: {}",
					addr, e
				);
				let report = format!("outputs by pmmr index: {}", e);
				Err(libwallet::ErrorKind::ClientCallback(report))?
			}
		}
	}
}

/// Call the wallet API to create a coinbase output for the given block_fees.
/// Will retry based on default "retry forever with backoff" behavior.
pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
	let url = format!("{}/v1/wallet/foreign/build_coinbase", dest);
	match single_create_coinbase(&url, &block_fees) {
		Err(e) => {
			error!(
				"Failed to get coinbase from {}. Run grin wallet listen?",
				url
			);
			error!("Underlying Error: {}", e.cause().unwrap());
			error!("Backtrace: {}", e.backtrace().unwrap());
			Err(e)?
		}
		Ok(res) => Ok(res),
	}
}

/// Makes a single request to the wallet API to create a new coinbase output.
fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result<CbData, Error> {
	let res = api::client::post(url, None, block_fees).context(ErrorKind::GenericError(
		"Posting create coinbase".to_string(),
	))?;
	Ok(res)
}
<|endoftext|>


<commit_before>use serde::{
    Deserialize,
    Serialize,
};
use std::path::Path;

use serde_with::CommaSeparator;

use anyhow::Result;
use rnc_core::grouper;
use phf::phf_map;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Precompute {
    pub id: usize,
    description: String,
    rna_type: String,
    has_coordinates: bool,
    so_rna_type: String,

    #[serde(with = "serde_with::rust::StringWithSeparator::<CommaSeparator>")]
    databases: Vec<String>,
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PrecomputeSummary {
    description: String,
    rna_type: String,
    has_coordinates: bool,
    databases: Vec<String>,
}

static SO_MAPPING: phf::Map<&'static str, &'static str> = phf_map! {
    "SO:0001263" => "SO:0000655",
    "SO:0001637" => "SO:0000252",
    "SO:0001427" => "SO:0000673",
    "SO:1001268" => "SO:0000673",
    "SO:0000122" => "SO:0000673",
    "SO:0005836" => "SO:0000673",
    "SO:0000185" => "SO:0000673",
    "SO:0000204" => "SO:0000673",
    "SO:0000140" => "SO:0000673",
    "SO:0000243" => "SO:0000673",
    "SO:0000205" => "SO:0000673",
    "SO:1001274" => "SO:0000673",
    "SO:0000726" => "SO:0000673",
    "SO:0000077" => "SO:0000673",
    "SO:0000836" => "SO:0000673",
    "SO:0000233" => "SO:0000673",
};

impl Precompute {
    pub fn so_rna_type(&self) -> &str {
        let given: &str = &self.so_rna_type;
        SO_MAPPING.get(given).cloned().unwrap_or(given)
    }
}

impl From<Precompute> for PrecomputeSummary {
    fn from(pre: Precompute) -> PrecomputeSummary {
        Self {
            description: pre.description,
            rna_type: pre.rna_type.replace("_", " "),
            has_coordinates: pre.has_coordinates,
            databases: pre.databases,
        }
    }
}

impl Precompute {
    pub fn has_coordiante_string(self) -> String {
        match self.has_coordinates {
            true => String::from("True"),
            false => String::from("False"),
        }
    }
}

impl grouper::HasIndex for Precompute {
    fn index(&self) -> usize {
        self.id
    }
}

pub fn group(path: &Path, max: usize, output: &Path) -> Result<()> {
    grouper::group::<Precompute>(grouper::Criteria::ExactlyOne, &path, 1, max, &output)
}
<commit_msg>Another renaming for SO terms<commit_after>use serde::{
    Deserialize,
    Serialize,
};
use std::path::Path;

use serde_with::CommaSeparator;

use anyhow::Result;
use rnc_core::grouper;
use phf::phf_map;

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Precompute {
    pub id: usize,
    description: String,
    rna_type: String,
    has_coordinates: bool,
    so_rna_type: String,

    #[serde(with = "serde_with::rust::StringWithSeparator::<CommaSeparator>")]
    databases: Vec<String>,
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PrecomputeSummary {
    description: String,
    rna_type: String,
    has_coordinates: bool,
    databases: Vec<String>,
}

static SO_MAPPING: phf::Map<&'static str, &'static str> = phf_map! {
    "SO:0001263" => "SO:0000655",
    "SO:0001637" => "SO:0000252",
    "SO:0001427" => "SO:0000673",
    "SO:1001268" => "SO:0000673",
    "SO:0000122" => "SO:0000673",
    "SO:0005836" => "SO:0000673",
    "SO:0000185" => "SO:0000673",
    "SO:0000204" => "SO:0000673",
    "SO:0000140" => "SO:0000673",
    "SO:0000243" => "SO:0000673",
    "SO:0000205" => "SO:0000673",
    "SO:1001274" => "SO:0000673",
    "SO:0000726" => "SO:0000673",
    "SO:0000077" => "SO:0000673",
    "SO:0000836" => "SO:0000673",
    "SO:0000233" => "SO:0000673",
    "SO:0001267" => "SO:0000275",
};

impl Precompute {
    pub fn so_rna_type(&self) -> &str {
        let given: &str = &self.so_rna_type;
        SO_MAPPING.get(given).cloned().unwrap_or(given)
    }
}

impl From<Precompute> for PrecomputeSummary {
    fn from(pre: Precompute) -> PrecomputeSummary {
        Self {
            description: pre.description,
            rna_type: pre.rna_type.replace("_", " "),
            has_coordinates: pre.has_coordinates,
            databases: pre.databases,
        }
    }
}

impl Precompute {
    pub fn has_coordiante_string(self) -> String {
        match self.has_coordinates {
            true => String::from("True"),
            false => String::from("False"),
        }
    }
}

impl grouper::HasIndex for Precompute {
    fn index(&self) -> usize {
        self.id
    }
}

pub fn group(path: &Path, max: usize, output: &Path) -> Result<()> {
    grouper::group::<Precompute>(grouper::Criteria::ExactlyOne, &path, 1, max, &output)
}
<|endoftext|>


<commit_before>

enum Number{
    Value(i32),
    Nothing
}

/* NOTE:

Q: Why to return 'Number' enum instead of an 32 ?
Ans: How would you return failure cases and integers at the same time ?
     Let's say the vector is empty, How would return the failure using an integer?
     Return -ve values ? Not an option.

     So we use enums.Enum specifies set of types (- optional values) it supports.
     Enums in rust are like unions (With just one extra field to specify correct
     current type during runtime [and optionally extract the value]). So memory is not wasted
     because of multiple types.
*/
fn vec_min(vec: Vec<i32>) -> Number{
    let mut min = Number::Nothing;

    for num in vec{
        match min{
            Number::Nothing => { min = Number::Value(num); }
            Number::Value(val) => {
                let new_min = min_num(val, num);
                min = Number::Value(new_min);
            }
        }
    }

    min
}

fn min_num(n1: i32, n2: i32) -> i32{
    if n1 < n2{
        n1
    }
    else{
        n2
    }
}

fn main() {
    println!("Hello, world!");

    let numbers = vec![4,5,6,1,3,6,3,7,-1];
    let min = vec_min(numbers);

    match min{
        Number::Nothing => println!("Error printing minimum value"),
        Number::Value(val) => println!("Minimum value = {}", val)
    }
}

/* NOTES:

Most of the stuff is an expression in Rust. I.e they evaluates to a value

* If - else blocks are expression => If else block evaluates to a value
* match is an expression
* Nested braces are expressions

*/
<commit_msg>Update main.rs<commit_after>

enum Number{
    Value(i32),
    Nothing
}

/* NOTE:

Q: Why to return 'Number' enum instead of an 32 ?
Ans: How would you return failure cases and integers at the same time ?
     Let's say the vector is empty, How would return the failure using an integer?
     Return -ve values ? Not an option.

     So we use enums.Enum specifies set of types (- optional values) it supports.
     Enums in rust are like unions (With just one extra field to specify correct
     current type during runtime [and optionally extract the value]). So memory is not wasted
     because of multiple types.
*/
fn vec_min(vec: Vec<i32>) -> Number{
    let mut min = Number::Nothing;

    for num in vec{
        match min{
            Number::Nothing => { min = Number::Value(num); }
            Number::Value(val) => {
                let new_min = min_num(val, num);
                min = Number::Value(new_min);
            }
        }
    }

    min
}

fn min_num(n1: i32, n2: i32) -> i32{
    if n1 < n2{
        n1
    }
    else{
        n2
    }
}

fn main() {
    println!("Hello, world!");

    let numbers = vec![4,5,6,1,3,6,3,7,-1];
    let min = vec_min(numbers);

    match min{
        Number::Nothing => println!("Error printing minimum value"),
        Number::Value(val) => println!("Minimum value = {}", val)
    }
}

/* NOTES:

Most of the stuff is an expression in Rust. I.e they evaluates to a value

* If - else blocks are expression => If else block evaluates to a value
* match is an expression
* Nested braces are expressions
* Entire funtion body is an expression

FOR BLOCKS:

Multiple expressions in a  { } (except match) should be separated by ;. Hence the line become statement.
But line interals can be an expression. Statements return ().

            ------ expression ------
let hello = match min{X => 5,Y => 20};
------------- statement --------------

Combined, all the lines separated by ; inside {} will create an expression.

                   --- exp 5 ---          ---- exp 9 --
let a = if 1 < 2 { 1; 2; 3; 4; 5 } else { 4; 6; 7; 8; 9  };
        --------------------- exp 5 ---------------------

Expression value of lines separated by ; in {} is the value of last expression.

*/
<|endoftext|>


<commit_before>// Copyright 2017 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ash::vk;
use core::buffer;
use core::command::ClearColor;
use core::format::{SurfaceType, ChannelType};
use core::memory::{self, ImageAccess, ImageLayout};
use core::pass::{AttachmentLoadOp, AttachmentStoreOp, AttachmentLayout};
use core::pso::{self, PipelineStage};
use core::IndexType;

pub fn map_format(surface: SurfaceType, chan: ChannelType) -> Option<vk::Format> {
    use core::format::SurfaceType::*;
    use core::format::ChannelType::*;
    Some(match surface {
        R4_G4 => match chan {
            Unorm => vk::Format::R4g4UnormPack8,
            _ => return None,
        },
        R4_G4_B4_A4 => match chan {
            Unorm => vk::Format::R4g4b4a4UnormPack16,
            _ => return None,
        },
        R5_G5_B5_A1 => match chan {
            Unorm => vk::Format::R5g5b5a1UnormPack16,
             _ => return None,
        },
        R5_G6_B5 => match chan {
            Unorm => vk::Format::R5g6b5UnormPack16,
             _ => return None,
        },
        R8 => match chan {
            Int   => vk::Format::R8Sint,
            Uint  => vk::Format::R8Uint,
            Inorm => vk::Format::R8Snorm,
            Unorm => vk::Format::R8Unorm,
            Srgb  => vk::Format::R8Srgb,
            _ => return None,
        },
        R8_G8 => match chan {
            Int   => vk::Format::R8g8Sint,
            Uint  => vk::Format::R8g8Uint,
            Inorm => vk::Format::R8g8Snorm,
            Unorm => vk::Format::R8g8Unorm,
            Srgb  => vk::Format::R8g8Srgb,
            _ => return None,
        },
        R8_G8_B8_A8 => match chan {
            Int   => vk::Format::R8g8b8a8Sint,
            Uint  => vk::Format::R8g8b8a8Uint,
            Inorm => vk::Format::R8g8b8a8Snorm,
            Unorm => vk::Format::R8g8b8a8Unorm,
            Srgb  => vk::Format::R8g8b8a8Srgb,
            _ => return None,
        },
        R10_G10_B10_A2 => match chan {
            Int   => vk::Format::A2r10g10b10SintPack32,
            Uint  => vk::Format::A2r10g10b10UintPack32,
            Inorm => vk::Format::A2r10g10b10SnormPack32,
            Unorm => vk::Format::A2r10g10b10UnormPack32,
            _ => return None,
        },
        R11_G11_B10 => match chan {
            Float => vk::Format::B10g11r11UfloatPack32,
            _ => return None,
        },
        R16 => match chan {
            Int   => vk::Format::R16Sint,
            Uint  => vk::Format::R16Uint,
            Inorm => vk::Format::R16Snorm,
            Unorm => vk::Format::R16Unorm,
            Float => vk::Format::R16Sfloat,
            _ => return None,
        },
        R16_G16 => match chan {
            Int   => vk::Format::R16g16Sint,
            Uint  => vk::Format::R16g16Uint,
            Inorm => vk::Format::R16g16Snorm,
            Unorm => vk::Format::R16g16Unorm,
            Float => vk::Format::R16g16Sfloat,
            _ => return None,
        },
        R16_G16_B16 => match chan {
            Int   => vk::Format::R16g16b16Sint,
            Uint  => vk::Format::R16g16b16Uint,
            Inorm => vk::Format::R16g16b16Snorm,
            Unorm => vk::Format::R16g16b16Unorm,
            Float => vk::Format::R16g16b16Sfloat,
            _ => return None,
        },
        R16_G16_B16_A16 => match chan {
            Int   => vk::Format::R16g16b16a16Sint,
            Uint  => vk::Format::R16g16b16a16Uint,
            Inorm => vk::Format::R16g16b16a16Snorm,
            Unorm => vk::Format::R16g16b16a16Unorm,
            Float => vk::Format::R16g16b16a16Sfloat,
            _ => return None,
        },
        R32 => match chan {
            Int   => vk::Format::R32Sint,
            Uint  => vk::Format::R32Uint,
            Float => vk::Format::R32Sfloat,
            _ => return None,
        },
        R32_G32 => match chan {
            Int   => vk::Format::R32g32Sint,
            Uint  => vk::Format::R32g32Uint,
            Float => vk::Format::R32g32Sfloat,
            _ => return None,
        },
        R32_G32_B32 => match chan {
            Int   => vk::Format::R32g32b32Sint,
            Uint  => vk::Format::R32g32b32Uint,
            Float => vk::Format::R32g32b32Sfloat,
            _ => return None,
        },
        R32_G32_B32_A32 => match chan {
            Int   => vk::Format::R32g32b32a32Sint,
            Uint  => vk::Format::R32g32b32a32Uint,
            Float => vk::Format::R32g32b32a32Sfloat,
            _ => return None,
        },
        B8_G8_R8_A8 => match chan {
            Unorm => vk::Format::B8g8r8a8Unorm,
            _ => return None,
        },
        D16 => match chan {
            Unorm  => vk::Format::D16Unorm,
            _ => return None,
        },
        D24 => match chan {
            Unorm => vk::Format::X8D24UnormPack32,
            _ => return None,
        },
        D24_S8 => match chan {
            Unorm => vk::Format::D24UnormS8Uint,
            _ => return None,
        },
        D32 => match chan {
            Float => vk::Format::D32Sfloat,
            _ => return None,
        },
    })
}

pub fn map_clear_color(value: ClearColor) -> vk::ClearColorValue {
    match value {
        ClearColor::Float(v) => vk::ClearColorValue::new_float32(v),
        ClearColor::Int(v)   => vk::ClearColorValue::new_int32(v),
        ClearColor::Uint(v)  => vk::ClearColorValue::new_uint32(v),
    }
}

pub fn map_attachment_load_op(op: AttachmentLoadOp) -> vk::AttachmentLoadOp {
    match op {
        AttachmentLoadOp::Load => vk::AttachmentLoadOp::Load,
        AttachmentLoadOp::Clear => vk::AttachmentLoadOp::Clear,
        AttachmentLoadOp::DontCare => vk::AttachmentLoadOp::DontCare,
    }
}

pub fn map_attachment_store_op(op: AttachmentStoreOp) -> vk::AttachmentStoreOp {
    match op {
        AttachmentStoreOp::Store => vk::AttachmentStoreOp::Store,
        AttachmentStoreOp::DontCare => vk::AttachmentStoreOp::DontCare,
    }
}

pub fn map_image_layout(layout: ImageLayout) -> vk::ImageLayout {
    match layout {
        ImageLayout::General => vk::ImageLayout::General,
        ImageLayout::ColorAttachmentOptimal => vk::ImageLayout::ColorAttachmentOptimal,
        ImageLayout::DepthStencilAttachmentOptimal => vk::ImageLayout::DepthStencilAttachmentOptimal,
        ImageLayout::DepthStencilReadOnlyOptimal => vk::ImageLayout::DepthStencilReadOnlyOptimal,
        ImageLayout::ShaderReadOnlyOptimal => vk::ImageLayout::ShaderReadOnlyOptimal,
        ImageLayout::TransferSrcOptimal => vk::ImageLayout::TransferSrcOptimal,
        ImageLayout::TransferDstOptimal => vk::ImageLayout::TransferDstOptimal,
        ImageLayout::Undefined => vk::ImageLayout::Undefined,
        ImageLayout::Preinitialized => vk::ImageLayout::Preinitialized,
        ImageLayout::Present => vk::ImageLayout::PresentSrcKhr,
    }
}

pub fn map_image_access(access: ImageAccess) -> vk::AccessFlags {
    let mut flags = vk::AccessFlags::empty();

    if access.contains(memory::RENDER_TARGET_CLEAR) {
        unimplemented!()
    }
    if access.contains(memory::RESOLVE_SRC) {
        unimplemented!()
    }
    if access.contains(memory::RESOLVE_DST) {
        unimplemented!()
    }
    if access.contains(memory::COLOR_ATTACHMENT_READ) {
        flags |= vk::ACCESS_COLOR_ATTACHMENT_READ_BIT;
    }
    if access.contains(memory::COLOR_ATTACHMENT_WRITE) {
        flags |= vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    }

    flags
}

pub fn map_pipeline_stage(stage: PipelineStage) -> vk::PipelineStageFlags {
    let mut flags = vk::PipelineStageFlags::empty();

    if stage.contains(pso::TOP_OF_PIPE) {
        flags |= vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    }
    if stage.contains(pso::DRAW_INDIRECT) {
        flags |= vk::PIPELINE_STAGE_DRAW_INDIRECT_BIT;
    }
    if stage.contains(pso::VERTEX_INPUT) {
        flags |= vk::PIPELINE_STAGE_VERTEX_INPUT_BIT;
    }
    if stage.contains(pso::VERTEX_SHADER) {
        flags |= vk::PIPELINE_STAGE_VERTEX_SHADER_BIT;
    }
    if stage.contains(pso::TESSELLATION_CONTROL_SHADER) {
        flags |= vk::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
    }
    if stage.contains(pso::TESSELLATION_EVALUATION_SHADER) {
        flags |= vk::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
    }
    if stage.contains(pso::GEOMETRY_SHADER) {
        flags |= vk::PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
    }
    if stage.contains(pso::FRAGMENT_SHADER) {
        flags |= vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    }
    if stage.contains(pso::EARLY_FRAGMENT_TESTS) {
        flags |= vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    }
    if stage.contains(pso::LATE_FRAGMENT_TESTS) {
        flags |= vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    }
    if stage.contains(pso::COLOR_ATTACHMENT_OUTPUT) {
        flags |= vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    }
    if stage.contains(pso::COMPUTE_SHADER) {
        flags |= vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    }
    if stage.contains(pso::TRANSFER) {
        flags |= vk::PIPELINE_STAGE_TRANSFER_BIT;
    }
    if stage.contains(pso::BOTTOM_OF_PIPE) {
        flags |= vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    }

    flags
}

pub fn map_buffer_usage(usage: buffer::Usage) -> vk::BufferUsageFlags {
    let mut flags = vk::BufferUsageFlags::empty();

    if usage.contains(buffer::TRANSFER_SRC) {
        flags |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT;
    }
    if usage.contains(buffer::TRANSFER_DST) {
        flags |= vk::BUFFER_USAGE_TRANSFER_DST_BIT;
    }
    if usage.contains(buffer::CONSTANT) {
        flags |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT;
    }
    if usage.contains(buffer::INDEX) {
        flags |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT;
    }
    if usage.contains(buffer::INDIRECT) {
        flags |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT;
    }
    if usage.contains(buffer::VERTEX) {
        flags |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT;
    }

    flags
}

pub fn map_index_type(index_type: IndexType) -> vk::IndexType {
    match index_type {
        IndexType::U16 => vk::IndexType::Uint16,
        IndexType::U32 => vk::IndexType::Uint32,
    }
}
<commit_msg>[ll] Fix build due to pipeline stage renames<commit_after>// Copyright 2017 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use ash::vk;
use core::buffer;
use core::command::ClearColor;
use core::format::{SurfaceType, ChannelType};
use core::memory::{self, ImageAccess, ImageLayout};
use core::pass::{AttachmentLoadOp, AttachmentStoreOp, AttachmentLayout};
use core::pso::{self, PipelineStage};
use core::IndexType;

pub fn map_format(surface: SurfaceType, chan: ChannelType) -> Option<vk::Format> {
    use core::format::SurfaceType::*;
    use core::format::ChannelType::*;
    Some(match surface {
        R4_G4 => match chan {
            Unorm => vk::Format::R4g4UnormPack8,
            _ => return None,
        },
        R4_G4_B4_A4 => match chan {
            Unorm => vk::Format::R4g4b4a4UnormPack16,
            _ => return None,
        },
        R5_G5_B5_A1 => match chan {
            Unorm => vk::Format::R5g5b5a1UnormPack16,
             _ => return None,
        },
        R5_G6_B5 => match chan {
            Unorm => vk::Format::R5g6b5UnormPack16,
             _ => return None,
        },
        R8 => match chan {
            Int   => vk::Format::R8Sint,
            Uint  => vk::Format::R8Uint,
            Inorm => vk::Format::R8Snorm,
            Unorm => vk::Format::R8Unorm,
            Srgb  => vk::Format::R8Srgb,
            _ => return None,
        },
        R8_G8 => match chan {
            Int   => vk::Format::R8g8Sint,
            Uint  => vk::Format::R8g8Uint,
            Inorm => vk::Format::R8g8Snorm,
            Unorm => vk::Format::R8g8Unorm,
            Srgb  => vk::Format::R8g8Srgb,
            _ => return None,
        },
        R8_G8_B8_A8 => match chan {
            Int   => vk::Format::R8g8b8a8Sint,
            Uint  => vk::Format::R8g8b8a8Uint,
            Inorm => vk::Format::R8g8b8a8Snorm,
            Unorm => vk::Format::R8g8b8a8Unorm,
            Srgb  => vk::Format::R8g8b8a8Srgb,
            _ => return None,
        },
        R10_G10_B10_A2 => match chan {
            Int   => vk::Format::A2r10g10b10SintPack32,
            Uint  => vk::Format::A2r10g10b10UintPack32,
            Inorm => vk::Format::A2r10g10b10SnormPack32,
            Unorm => vk::Format::A2r10g10b10UnormPack32,
            _ => return None,
        },
        R11_G11_B10 => match chan {
            Float => vk::Format::B10g11r11UfloatPack32,
            _ => return None,
        },
        R16 => match chan {
            Int   => vk::Format::R16Sint,
            Uint  => vk::Format::R16Uint,
            Inorm => vk::Format::R16Snorm,
            Unorm => vk::Format::R16Unorm,
            Float => vk::Format::R16Sfloat,
            _ => return None,
        },
        R16_G16 => match chan {
            Int   => vk::Format::R16g16Sint,
            Uint  => vk::Format::R16g16Uint,
            Inorm => vk::Format::R16g16Snorm,
            Unorm => vk::Format::R16g16Unorm,
            Float => vk::Format::R16g16Sfloat,
            _ => return None,
        },
        R16_G16_B16 => match chan {
            Int   => vk::Format::R16g16b16Sint,
            Uint  => vk::Format::R16g16b16Uint,
            Inorm => vk::Format::R16g16b16Snorm,
            Unorm => vk::Format::R16g16b16Unorm,
            Float => vk::Format::R16g16b16Sfloat,
            _ => return None,
        },
        R16_G16_B16_A16 => match chan {
            Int   => vk::Format::R16g16b16a16Sint,
            Uint  => vk::Format::R16g16b16a16Uint,
            Inorm => vk::Format::R16g16b16a16Snorm,
            Unorm => vk::Format::R16g16b16a16Unorm,
            Float => vk::Format::R16g16b16a16Sfloat,
            _ => return None,
        },
        R32 => match chan {
            Int   => vk::Format::R32Sint,
            Uint  => vk::Format::R32Uint,
            Float => vk::Format::R32Sfloat,
            _ => return None,
        },
        R32_G32 => match chan {
            Int   => vk::Format::R32g32Sint,
            Uint  => vk::Format::R32g32Uint,
            Float => vk::Format::R32g32Sfloat,
            _ => return None,
        },
        R32_G32_B32 => match chan {
            Int   => vk::Format::R32g32b32Sint,
            Uint  => vk::Format::R32g32b32Uint,
            Float => vk::Format::R32g32b32Sfloat,
            _ => return None,
        },
        R32_G32_B32_A32 => match chan {
            Int   => vk::Format::R32g32b32a32Sint,
            Uint  => vk::Format::R32g32b32a32Uint,
            Float => vk::Format::R32g32b32a32Sfloat,
            _ => return None,
        },
        B8_G8_R8_A8 => match chan {
            Unorm => vk::Format::B8g8r8a8Unorm,
            _ => return None,
        },
        D16 => match chan {
            Unorm  => vk::Format::D16Unorm,
            _ => return None,
        },
        D24 => match chan {
            Unorm => vk::Format::X8D24UnormPack32,
            _ => return None,
        },
        D24_S8 => match chan {
            Unorm => vk::Format::D24UnormS8Uint,
            _ => return None,
        },
        D32 => match chan {
            Float => vk::Format::D32Sfloat,
            _ => return None,
        },
    })
}

pub fn map_clear_color(value: ClearColor) -> vk::ClearColorValue {
    match value {
        ClearColor::Float(v) => vk::ClearColorValue::new_float32(v),
        ClearColor::Int(v)   => vk::ClearColorValue::new_int32(v),
        ClearColor::Uint(v)  => vk::ClearColorValue::new_uint32(v),
    }
}

pub fn map_attachment_load_op(op: AttachmentLoadOp) -> vk::AttachmentLoadOp {
    match op {
        AttachmentLoadOp::Load => vk::AttachmentLoadOp::Load,
        AttachmentLoadOp::Clear => vk::AttachmentLoadOp::Clear,
        AttachmentLoadOp::DontCare => vk::AttachmentLoadOp::DontCare,
    }
}

pub fn map_attachment_store_op(op: AttachmentStoreOp) -> vk::AttachmentStoreOp {
    match op {
        AttachmentStoreOp::Store => vk::AttachmentStoreOp::Store,
        AttachmentStoreOp::DontCare => vk::AttachmentStoreOp::DontCare,
    }
}

pub fn map_image_layout(layout: ImageLayout) -> vk::ImageLayout {
    match layout {
        ImageLayout::General => vk::ImageLayout::General,
        ImageLayout::ColorAttachmentOptimal => vk::ImageLayout::ColorAttachmentOptimal,
        ImageLayout::DepthStencilAttachmentOptimal => vk::ImageLayout::DepthStencilAttachmentOptimal,
        ImageLayout::DepthStencilReadOnlyOptimal => vk::ImageLayout::DepthStencilReadOnlyOptimal,
        ImageLayout::ShaderReadOnlyOptimal => vk::ImageLayout::ShaderReadOnlyOptimal,
        ImageLayout::TransferSrcOptimal => vk::ImageLayout::TransferSrcOptimal,
        ImageLayout::TransferDstOptimal => vk::ImageLayout::TransferDstOptimal,
        ImageLayout::Undefined => vk::ImageLayout::Undefined,
        ImageLayout::Preinitialized => vk::ImageLayout::Preinitialized,
        ImageLayout::Present => vk::ImageLayout::PresentSrcKhr,
    }
}

pub fn map_image_access(access: ImageAccess) -> vk::AccessFlags {
    let mut flags = vk::AccessFlags::empty();

    if access.contains(memory::RENDER_TARGET_CLEAR) {
        unimplemented!()
    }
    if access.contains(memory::RESOLVE_SRC) {
        unimplemented!()
    }
    if access.contains(memory::RESOLVE_DST) {
        unimplemented!()
    }
    if access.contains(memory::COLOR_ATTACHMENT_READ) {
        flags |= vk::ACCESS_COLOR_ATTACHMENT_READ_BIT;
    }
    if access.contains(memory::COLOR_ATTACHMENT_WRITE) {
        flags |= vk::ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    }

    flags
}

pub fn map_pipeline_stage(stage: PipelineStage) -> vk::PipelineStageFlags {
    let mut flags = vk::PipelineStageFlags::empty();

    if stage.contains(pso::TOP_OF_PIPE) {
        flags |= vk::PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    }
    if stage.contains(pso::DRAW_INDIRECT) {
        flags |= vk::PIPELINE_STAGE_DRAW_INDIRECT_BIT;
    }
    if stage.contains(pso::VERTEX_INPUT) {
        flags |= vk::PIPELINE_STAGE_VERTEX_INPUT_BIT;
    }
    if stage.contains(pso::VERTEX_SHADER) {
        flags |= vk::PIPELINE_STAGE_VERTEX_SHADER_BIT;
    }
    if stage.contains(pso::HULL_SHADER) {
        flags |= vk::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT;
    }
    if stage.contains(pso::DOMAIN_SHADER) {
        flags |= vk::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT;
    }
    if stage.contains(pso::GEOMETRY_SHADER) {
        flags |= vk::PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
    }
    if stage.contains(pso::PIXEL_SHADER) {
        flags |= vk::PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    }
    if stage.contains(pso::EARLY_FRAGMENT_TESTS) {
        flags |= vk::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    }
    if stage.contains(pso::LATE_FRAGMENT_TESTS) {
        flags |= vk::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    }
    if stage.contains(pso::COLOR_ATTACHMENT_OUTPUT) {
        flags |= vk::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    }
    if stage.contains(pso::COMPUTE_SHADER) {
        flags |= vk::PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    }
    if stage.contains(pso::TRANSFER) {
        flags |= vk::PIPELINE_STAGE_TRANSFER_BIT;
    }
    if stage.contains(pso::BOTTOM_OF_PIPE) {
        flags |= vk::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    }

    flags
}

pub fn map_buffer_usage(usage: buffer::Usage) -> vk::BufferUsageFlags {
    let mut flags = vk::BufferUsageFlags::empty();

    if usage.contains(buffer::TRANSFER_SRC) {
        flags |= vk::BUFFER_USAGE_TRANSFER_SRC_BIT;
    }
    if usage.contains(buffer::TRANSFER_DST) {
        flags |= vk::BUFFER_USAGE_TRANSFER_DST_BIT;
    }
    if usage.contains(buffer::CONSTANT) {
        flags |= vk::BUFFER_USAGE_UNIFORM_BUFFER_BIT;
    }
    if usage.contains(buffer::INDEX) {
        flags |= vk::BUFFER_USAGE_INDEX_BUFFER_BIT;
    }
    if usage.contains(buffer::INDIRECT) {
        flags |= vk::BUFFER_USAGE_INDIRECT_BUFFER_BIT;
    }
    if usage.contains(buffer::VERTEX) {
        flags |= vk::BUFFER_USAGE_VERTEX_BUFFER_BIT;
    }

    flags
}

pub fn map_index_type(index_type: IndexType) -> vk::IndexType {
    match index_type {
        IndexType::U16 => vk::IndexType::Uint16,
        IndexType::U32 => vk::IndexType::Uint32,
    }
}
<|endoftext|>


<commit_before>use std::mem;

use std::collections::vec_deque::{Iter, IterMut};
use std::collections::VecDeque;

pub struct LimitedQueue<T> {
	q: VecDeque<T>,
	limit: usize,
}

impl<T> LimitedQueue<T> {

	pub fn new(limit: usize) -> LimitedQueue<T>
	{
		LimitedQueue { q: VecDeque::new(), limit: limit }
	}

	pub fn with_allocation(limit: usize) -> LimitedQueue<T>
	{
		LimitedQueue { q: VecDeque::with_capacity(limit), limit: limit }
	}

	pub fn push(&mut self, item: T)
	{
		self.q.push_back(item);
		while self.q.len() > self.limit {
			self.q.pop_front();
		}
	}

	pub fn iter(&self) -> Iter<T> { self.q.iter() }
	pub fn iter_mut(&mut self) -> IterMut<T> { self.q.iter_mut() }
	pub fn front(&self) -> Option<&T> { self.q.front() }
	pub fn front_mut(&mut self) -> Option<&mut T> { self.q.front_mut() }
	pub fn back(&self) -> Option<&T> { self.q.back() }
	pub fn back_mut(&mut self) -> Option<&mut T> { self.q.back_mut() }
}

pub struct IndexedLimitedQueue<D> {
	buffer: LimitedQueue<(String, D)>,
}

impl<D> IndexedLimitedQueue<D> {

	pub fn new(limit: usize) -> IndexedLimitedQueue<D>
	{
		assert!(limit > 0);
		IndexedLimitedQueue { buffer: LimitedQueue::new(limit) }
	}

	pub fn with_allocation(limit: usize) -> IndexedLimitedQueue<D>
	{
		assert!(limit > 0);
		IndexedLimitedQueue { buffer: LimitedQueue::with_allocation(limit) }
	}

	pub fn get<I>(&self, idx: I) -> Option<&D>
		where I: PartialEq<String>
	{
		self.buffer.iter().find(|ref e| idx == e.0).map(|ref e| &e.1)
	}

	pub fn get_mut<I>(&mut self, idx: I) -> Option<&mut D>
		where I: PartialEq<String>
	{
		self.buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1)
	}

	pub fn get_mut_default<I>(&mut self, idx: I) -> &mut D
		where I: Into<String>, D: Default
	{
		let idx = idx.into();
		let buffer = &mut self.buffer;

		if buffer.iter().find(|ref e| idx == e.0).is_none() {
			buffer.push((idx, Default::default()));
			return buffer.back_mut().map(|e| &mut e.1).expect("always exists");
		}

		buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1).expect("always exists")
	}

	pub fn upsert<I>(&mut self, idx: I, mut data: D) -> Option<D>
		where I: Into<String>
	{
		let idx = idx.into();
		let buffer = &mut self.buffer;

		if let Some(it) = buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1) {
			mem::swap(it, &mut data);
			return Some(data);
		}

		buffer.push((idx, data));
		None
	}
}
<commit_msg>LimitedQueue: Remove unnecessary copying of 'idx'<commit_after>use std::mem;

use std::collections::vec_deque::{Iter, IterMut};
use std::collections::VecDeque;

pub struct LimitedQueue<T> {
	q: VecDeque<T>,
	limit: usize,
}

impl<T> LimitedQueue<T> {

	pub fn new(limit: usize) -> LimitedQueue<T>
	{
		LimitedQueue { q: VecDeque::new(), limit: limit }
	}

	pub fn with_allocation(limit: usize) -> LimitedQueue<T>
	{
		LimitedQueue { q: VecDeque::with_capacity(limit), limit: limit }
	}

	pub fn push(&mut self, item: T)
	{
		self.q.push_back(item);
		while self.q.len() > self.limit {
			self.q.pop_front();
		}
	}

	pub fn iter(&self) -> Iter<T> { self.q.iter() }
	pub fn iter_mut(&mut self) -> IterMut<T> { self.q.iter_mut() }
	pub fn front(&self) -> Option<&T> { self.q.front() }
	pub fn front_mut(&mut self) -> Option<&mut T> { self.q.front_mut() }
	pub fn back(&self) -> Option<&T> { self.q.back() }
	pub fn back_mut(&mut self) -> Option<&mut T> { self.q.back_mut() }
}

pub struct IndexedLimitedQueue<D> {
	buffer: LimitedQueue<(String, D)>,
}

impl<D> IndexedLimitedQueue<D> {

	pub fn new(limit: usize) -> IndexedLimitedQueue<D>
	{
		assert!(limit > 0);
		IndexedLimitedQueue { buffer: LimitedQueue::new(limit) }
	}

	pub fn with_allocation(limit: usize) -> IndexedLimitedQueue<D>
	{
		assert!(limit > 0);
		IndexedLimitedQueue { buffer: LimitedQueue::with_allocation(limit) }
	}

	pub fn get<I>(&self, idx: I) -> Option<&D>
		where I: PartialEq<String>
	{
		self.buffer.iter().find(|ref e| idx == e.0).map(|ref e| &e.1)
	}

	pub fn get_mut<I>(&mut self, idx: I) -> Option<&mut D>
		where I: PartialEq<String>
	{
		self.buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1)
	}

	pub fn get_mut_default<I>(&mut self, idx: I) -> &mut D
		where I: PartialEq<String> + Into<String>, D: Default
	{
		if self.buffer.iter().find(|ref e| idx == e.0).is_none() {
			self.buffer.push((idx.into(), Default::default()));
			self.buffer.back_mut().map(|e| &mut e.1).expect("always exists")
		} else {
			self.buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1).expect("always exists")
		}
	}

	pub fn upsert<I>(&mut self, idx: I, mut data: D) -> Option<D>
		where I: PartialEq<String> + Into<String>
	{
		if let Some(it) = self.buffer.iter_mut().find(|ref e| idx == e.0).map(|e| &mut e.1) {
			mem::swap(it, &mut data);
			return Some(data);
		}

		self.buffer.push((idx.into(), data));
		None
	}
}
<|endoftext|>


<commit_before>use std::error;
use std::time::Duration;
use std::thread::sleep;

use hyper;
use rusoto_core::{default_tls_client, DefaultCredentialsProvider, Region};
use rusoto_ecs::EcsClient;

use config;
use output;

use super::super::error::CommandError;
use command::ecs::Executer as EcsExecuter;
use command::ecs::TaskDescription;

pub struct ExecuterOptions {
    pub no_wait: bool,
}

pub struct Executer<'c> {
    ecs_client: EcsClient<DefaultCredentialsProvider, hyper::client::Client>,
    config: &'c config::command::RunTaskConfig,
    options: &'c ExecuterOptions,
}

impl<'c> Executer<'c> {
    pub fn from_config(
        config: &'c config::command::RunTaskConfig,
        options: &'c ExecuterOptions,
    ) -> Self {
        trace!("command::run_task::Executer::from_config");

        let credentials = DefaultCredentialsProvider::new().unwrap();
        let client = EcsClient::new(
            default_tls_client().unwrap(),
            credentials,
            Region::ApNortheast1,
        );
        Executer {
            ecs_client: client,
            config: config,
            options: options,
        }
    }

    pub fn run(&self) -> Result<(), Box<error::Error>> {
        trace!("command::run_task::Executer::run");

        output::PrintLine::info("Registering a task definition");
        let task_definition = try!(self.register_task_definition(&self.config.task_definition));
        let task_definition_arn = try!(
            task_definition
                .task_definition_arn
                .as_ref()
                .ok_or(Box::new(CommandError::Unknown,),)
        );

        output::PrintLine::info("Starting to run the task");
        let running_task = try!(self.run_task(&self.config.cluster, &task_definition_arn));

        if !self.options.no_wait {
            try!(self.wait_for_stopped(&running_task));
        }

        output::PrintLine::success("Finished running the task");
        Ok(())
    }

    fn wait_for_stopped(&self, running_task: &TaskDescription) -> Result<(), Box<error::Error>> {
        trace!("command::run-task::Executer::wait_for_stopped");

        fn check_stopped(current_task: &TaskDescription) -> Result<bool, Box<error::Error>> {

            if let Some(failure) = current_task.failure.as_ref() {
                let reason = failure.reason.as_ref().map(String::as_str).unwrap_or("");
                output::PrintLine::error(&format!("Finished task with error :{}", reason));
                return Err(Box::new(CommandError::Unknown));
            }

            match current_task.task.as_ref() {
                None => {
                    output::PrintLine::error("No task found");
                    return Err(Box::new(CommandError::Unknown));
                }
                Some(task) => {
                    let status = try!(
                        task.last_status
                            .as_ref()
                            .ok_or(Box::new(CommandError::Unknown))
                    );
                    if status == "STOPPED" {
                        let essential_container = try!(
                            task.containers
                                .as_ref()
                                .and_then(|c| c.first())
                                .ok_or(Box::new(CommandError::Unknown))
                        );
                        match essential_container.exit_code {
                            Some(0) => return Ok(true), // stopped task successfully!
                            Some(code) => {
                                output::PrintLine::error(&format!(
                                    "The container in the task exited with code: {}",
                                    code
                                ));
                                return Err(Box::new(CommandError::Unknown));
                            }
                            None => {
                                let reason = essential_container
                                    .reason
                                    .as_ref()
                                    .map(String::as_str)
                                    .unwrap_or("");
                                output::PrintLine::error(
                                    &format!("Failed running task by some reason: {}", reason),
                                );
                                return Err(Box::new(CommandError::Unknown));
                            }
                        }
                    }
                }
            };

            // running task yet
            Ok(false)
        }

        let stopped = try!(check_stopped(running_task));
        if stopped {
            return Ok(());
        }

        let task_arn = &running_task
            .task
            .as_ref()
            .unwrap()
            .task_arn
            .as_ref()
            .unwrap();

        // TODO: Timeout
        loop {
            output::PrintLine::info("Waiting for the task to be stopped...");
            sleep(Duration::from_millis(2000));

            let current_task = try!(self.describe_task(&self.config.cluster, task_arn));
            let stopped = try!(check_stopped(&current_task));
            if stopped {
                return Ok(());
            }
        }

        // Ok(())
    }
}

impl<'c> EcsExecuter for Executer<'c> {
    fn ecs_client(&self) -> &EcsClient<DefaultCredentialsProvider, hyper::client::Client> {
        &self.ecs_client
    }
}
<commit_msg>Check stop reason of task<commit_after>use std::error;
use std::time::Duration;
use std::thread::sleep;

use hyper;
use rusoto_core::{default_tls_client, DefaultCredentialsProvider, Region};
use rusoto_ecs::EcsClient;

use config;
use output;

use super::super::error::CommandError;
use command::ecs::Executer as EcsExecuter;
use command::ecs::TaskDescription;

pub struct ExecuterOptions {
    pub no_wait: bool,
}

pub struct Executer<'c> {
    ecs_client: EcsClient<DefaultCredentialsProvider, hyper::client::Client>,
    config: &'c config::command::RunTaskConfig,
    options: &'c ExecuterOptions,
}

impl<'c> Executer<'c> {
    pub fn from_config(
        config: &'c config::command::RunTaskConfig,
        options: &'c ExecuterOptions,
    ) -> Self {
        trace!("command::run_task::Executer::from_config");

        let credentials = DefaultCredentialsProvider::new().unwrap();
        let client = EcsClient::new(
            default_tls_client().unwrap(),
            credentials,
            Region::ApNortheast1,
        );
        Executer {
            ecs_client: client,
            config: config,
            options: options,
        }
    }

    pub fn run(&self) -> Result<(), Box<error::Error>> {
        trace!("command::run_task::Executer::run");

        output::PrintLine::info("Registering a task definition");
        let task_definition = try!(self.register_task_definition(&self.config.task_definition));
        let task_definition_arn = try!(
            task_definition
                .task_definition_arn
                .as_ref()
                .ok_or(Box::new(CommandError::Unknown,),)
        );

        output::PrintLine::info("Starting to run the task");
        let running_task = try!(self.run_task(&self.config.cluster, &task_definition_arn));

        if !self.options.no_wait {
            try!(self.wait_for_stopped(&running_task));
        }

        output::PrintLine::success("Finished running the task");
        Ok(())
    }

    fn wait_for_stopped(&self, running_task: &TaskDescription) -> Result<(), Box<error::Error>> {
        trace!("command::run-task::Executer::wait_for_stopped");

        fn check_stopped(current_task: &TaskDescription) -> Result<bool, Box<error::Error>> {
            if let Some(failure) = current_task.failure.as_ref() {
                let reason = failure.reason.as_ref().map(String::as_str).unwrap_or("");
                output::PrintLine::error(&format!("Finished task with error :{}", reason));
                return Err(Box::new(CommandError::Unknown));
            }

            match current_task.task.as_ref() {
                None => {
                    output::PrintLine::error("No task found");
                    return Err(Box::new(CommandError::Unknown));
                }
                Some(task) => {
                    let status = try!(
                        task.last_status
                            .as_ref()
                            .ok_or(Box::new(CommandError::Unknown))
                    );
                    if status == "STOPPED" {
                        if let Some(reason) = task.stopped_reason.as_ref() {
                            if reason != "Essential container in task exited" {
                                output::PrintLine::error(
                                    &format!("The task stopped with reason: {}", reason),
                                );
                                return Err(Box::new(CommandError::Unknown));
                            }
                        }

                        let essential_container = try!(
                            task.containers
                                .as_ref()
                                .and_then(|c| c.first())
                                .ok_or(Box::new(CommandError::Unknown))
                        );

                        match essential_container.exit_code {
                            Some(0) => return Ok(true), // stopped task successfully!
                            Some(code) => {
                                output::PrintLine::error(&format!(
                                    "The container in the task exited with code: {}",
                                    code
                                ));
                                return Err(Box::new(CommandError::Unknown));
                            }
                            None => {
                                let reason = essential_container
                                    .reason
                                    .as_ref()
                                    .map(String::as_str)
                                    .unwrap_or("");
                                output::PrintLine::error(
                                    &format!("Failed running task by some reason: {}", reason),
                                );
                                return Err(Box::new(CommandError::Unknown));
                            }
                        }
                    }
                }
            };

            // running task yet
            Ok(false)
        }

        let stopped = try!(check_stopped(running_task));
        if stopped {
            return Ok(());
        }

        let task_arn = &running_task
            .task
            .as_ref()
            .unwrap()
            .task_arn
            .as_ref()
            .unwrap();

        // TODO: Timeout
        loop {
            output::PrintLine::info("Waiting for the task to be stopped...");
            sleep(Duration::from_millis(2000));

            let current_task = try!(self.describe_task(&self.config.cluster, task_arn));
            let stopped = try!(check_stopped(&current_task));
            if stopped {
                return Ok(());
            }
        }

        // Ok(())
    }
}

impl<'c> EcsExecuter for Executer<'c> {
    fn ecs_client(&self) -> &EcsClient<DefaultCredentialsProvider, hyper::client::Client> {
        &self.ecs_client
    }
}
<|endoftext|>