<commit_before>use std::rand::Rng;
use calx::dijkstra::Dijkstra;
use calx::Rgb;
use world;
use location::{Location};
use dir6::Dir6;
use flags;
use components::{Intrinsic};
use components::{BrainState};
use geom::HexGeom;
use spatial::Place;
use action;
use fov::Fov;
use rng;
use msg;

/// Game object handle.
#[deriving(Copy, PartialEq, Eq, Clone, Hash, Show, Decodable, Encodable)]
pub struct Entity(pub uint);

impl Entity {
    /// Place the entity in a location in the game world.
    pub fn place(self, loc: Location) {
        world::with_mut(|w| w.spatial.insert_at(self, loc));
        self.on_move_to(loc);
    }

    /// Remove the entity from all the game world systems. THE ENTITY VALUE
    /// WILL BE INVALID FROM HERE ON AND USING IT WILL LEAD TO BUGS. THE
    /// CALLER IS RESPONSIBLE FOR ENSUING THAT AN ENTITY WILL NOT BE
    /// USED FROM ANYWHERE AFTER THE DELETE OPERATION.
    pub fn delete(self) {
        // COMPONENTS CHECKPOINT
        // This needs to call every component system.
        world::with_mut(|w| w.descs_mut().remove(self));
        world::with_mut(|w| w.map_memories_mut().remove(self));
        world::with_mut(|w| w.spawns_mut().remove(self));
        world::with_mut(|w| w.healths_mut().remove(self));
        world::with_mut(|w| w.brains_mut().remove(self));

        world::with_mut(|w| w.spatial.remove(self));

        world::with_mut(|w| w.ecs.delete(self));
    }

    pub fn blocks_walk(self) -> bool { self.is_mob() }

    pub fn name(self) -> String {
        world::with(|w|
            match w.descs().get(self) {
                Some(desc) => desc.name.clone(),
                None => "".to_string()
            }
        )
    }

    pub fn get_icon(self) -> Option<(uint, Rgb)> {
        world::with(|w|
            if let Some(desc) = w.descs().get(self) {
                Some((desc.icon, desc.color))
            } else {
                None
            }
        )
    }

    /// Create a cloned entity that uses the current entity as a parent
    /// prototype. Components not defined in the clone entity will be read
    /// from the current entity.
    pub fn clone_at(self, loc: Location) -> Entity {
        let ret = world::with_mut(|w| { w.ecs.new_entity(Some(self)) });
        ret.place(loc);
        ret
    }

// Spatial methods /////////////////////////////////////////////////////

    pub fn can_enter(self, loc: Location) -> bool {
        if self.is_mob() && loc.has_mobs() { return false; }
        if loc.terrain().is_door() && !self.has_intrinsic(Intrinsic::Hands) {
            // Can't open doors without hands.
            return false;
        }
        if loc.blocks_walk() { return false; }
        true
    }

    /// Return whether the entity can move in a direction.
    pub fn can_step(self, dir: Dir6) -> bool {
        let place = world::with(|w| w.spatial.get(self));
        if let Some(Place::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            return self.can_enter(new_loc);
        }
        return false;
    }

    /// Try to move the entity in direction.
    pub fn step(self, dir: Dir6) {
        let place = world::with(|w| w.spatial.get(self));
        if let Some(Place::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            if self.can_enter(new_loc) {
                world::with_mut(|w| w.spatial.insert_at(self, new_loc));
                self.on_move_to(new_loc);
            }
        }
    }

    pub fn location(self) -> Option<Location> {
        match world::with(|w| w.spatial.get(self)) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(e)) => e.location(),
            _ => None
        }
    }

    pub fn distance_from(self, other: Entity) -> Option<int> {
        if let (Some(loc1), Some(loc2)) = (self.location(), other.location()) {
            loc1.distance_from(loc2)
        } else {
            None
        }
    }

// Damage and lifetime /////////////////////////////////////////////////

    pub fn damage(self, amount: int) {
        if amount == 0 { return; }
        let max_hp = self.max_hp();

        let (_amount, kill) = world::with_mut(|w| {
            let health = w.healths_mut().get(self).expect("no health");
            health.wounds += amount;
            (amount, health.wounds >= max_hp)
        });

        msg::push_msg(::Msg::Damage(self));
        if kill {
            self.kill();
        }
    }

    /// Do any game logic stuff related to this entity dying violently before
    /// deleting it.
    pub fn kill(self) {
        msgln!("{} dies.", self.name());
        msg::push_msg(::Msg::Gib(self.location().expect("no location")));
        self.delete();
    }

// Mob methods /////////////////////////////////////////////////////////

    pub fn is_mob(self) -> bool {
        world::with(|w| w.brains().get(self).is_some()) && self.location().is_some()
    }

    /// Return whether this mob is the player avatar.
    pub fn is_player(self) -> bool {
        self.brain_state() == Some(BrainState::PlayerControl) && self.location().is_some()
    }

    /*
    pub fn has_status(self, status: Status) -> bool {
        world::with(|w| {
            if let Some(&mob) = w.mobs().get(self) {
                return mob.has_status(status);
            }
            return false;
        })
    }

    pub fn add_status(self, status: Status) {
        if !self.is_mob() { return; }
        world::with_mut(|w| w.mobs_mut().get(self).expect("no mob").add_status(status));
        assert!(self.has_status(status));
    }

    pub fn remove_status(self, status: Status) {
        if !self.is_mob() { return; }
        world::with_mut(|w| w.mobs_mut().get(self).expect("no mob").remove_status(status));
        assert!(!self.has_status(status));
    }
    */

    pub fn has_intrinsic(self, intrinsic: Intrinsic) -> bool {
        world::with(|w|
            if let Some(stat) = w.mob_stats().get(self) {
                stat.intrinsics & intrinsic as u32 != 0
            } else {
                false
            })
    }

    /// Return whether this entity is an awake mob.
    pub fn is_active(self) -> bool {
        match self.brain_state() {
            Some(BrainState::Asleep) => false,
            Some(_) => true,
            _ => false
        }
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    pub fn ticks_this_frame(self) -> bool {
        if self.is_player() { return true; }
        if !self.is_mob() { return false; }

        let tick = flags::get_tick();
        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = tick % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(Intrinsic::Fast),
            2 => return true,
            3 => return false,//self.has_status(Status::Quick),
            4 => return !self.has_intrinsic(Intrinsic::Slow)
                        /*&& !self.has_status(Status::Slow)*/,
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    pub fn acts_this_frame(self) -> bool {
        if !self.is_active() { return false; }
        return self.ticks_this_frame();
    }

    /// Return whether the entity is an awake non-player mob and should be
    /// animated with a bob.
    pub fn is_bobbing(self) -> bool {
        self.is_active() && !self.is_player()
    }

    pub fn melee(self, dir: Dir6) {
        let loc = self.location().expect("no location") + dir.to_v2();
        if let Some(e) = loc.mob_at() {
            // Attack power
            let p = self.power_level();
            if p == 0 {
                // No fight capacity.
                return;
            }

            // Every five points of power is one certain hit.
            let full = p / 5;
            // The fractional points are one probabilistic hit.
            let partial = (p % 5) as f64 / 5.0;

            let damage = full + if rng::p(partial) { 1 } else { 0 };
            e.damage(damage)
        }
    }

    pub fn power_level(self) -> int {
        world::with(|w|
            if let Some(stat) = w.mob_stats().get(self) { stat.power }
            else { 0 })
    }

    pub fn hp(self) -> int {
        self.max_hp() - world::with(|w|
            if let Some(health) = w.healths().get(self) {
                health.wounds
            } else {
                0
            })
    }

    pub fn max_hp(self) -> int {
        self.power_level()
    }

// AI methods /////////////////////////////////////////////////////////

    /// Top-level method called each frame to update the entity.
    pub fn update(self) {
        if self.is_mob() && !self.is_player() && self.ticks_this_frame() {
            self.mob_ai();
        }
    }

    fn brain_state(self) -> Option<BrainState> {
        world::with(|w| w.brains().get(self).map(|b| b.state))
    }

    fn set_brain_state(self, brain_state: BrainState) {
        world::with_mut(|w| w.brains_mut().get(self).expect("no brains").state = brain_state );
    }

    fn wake_up(self) {
        if self.brain_state() == Some(BrainState::Asleep) {
            self.set_brain_state(BrainState::Hunting);
        }
    }

    /// AI routine for autonomous mobs.
    fn mob_ai(self) {
        assert!(self.is_mob());
        assert!(!self.is_player());
        assert!(self.ticks_this_frame());

        if self.brain_state() == Some(BrainState::Asleep) {
            if let Some(p) = action::player() {
                // TODO: Line-of-sight, stealth concerns, other enemies than
                // player etc.
                if let Some(d) = p.distance_from(self) {
                    if d < 6 {
                        self.wake_up();
                    }
                }
            }

            return;
        }

        if let Some(p) = action::player() {
            let loc = self.location().expect("no location");

            let vec_to_enemy = loc.v2_at(p.location().expect("no location"));
            if let Some(v) = vec_to_enemy {
                if v.hex_dist() == 1 {
                    // Melee range, hit.
                    self.melee(Dir6::from_v2(v));
                } else {
                    // Walk towards.
                    let pathing_depth = 16;
                    let pathing = Dijkstra::new(
                        vec![p.location().expect("no location")], |&loc| !loc.blocks_walk(),
                        pathing_depth);

                    let steps = pathing.sorted_neighbors(&loc);
                    if steps.len() > 0 {
                        self.step(loc.dir6_towards(steps[0]).expect("No loc pair orientation"));
                    } else {
                        self.step(rng::with(|ref mut rng| rng.gen::<Dir6>()));
                        // TODO: Fall asleep if things get boring.
                    }
                }
            }
        }
    }

    /// Return whether this thing wants to fight the other thing.
    fn is_hostile_to(self, other: Entity) -> bool {
        if let Some(p) = action::player() {
            if self.is_mob() && other.is_mob() && self != other &&
                (self == p || other == p) {
                return true;
            }
        }
        return false;
    }

    /// Return whether the mob has hostiles in its immediate vicinity.
    pub fn is_threatened(self) -> bool {
        // XXX: Expensive.
        let range = 12u;
        let loc = self.location().expect("no location");
        let seen: Vec<Location> = Fov::new(
            |pt| (loc + pt).blocks_sight(), range)
            .map(|pt| loc + pt)
            .collect();
        for loc in seen.iter() {
            if let Some(m) = loc.mob_at() {
                if m.is_hostile_to(self) {
                    return true;
                }
            }
        }
        false
    }

// Callbacks ///////////////////////////////////////////////////////////

    /// Called after the entity is moved to a new location.
    pub fn on_move_to(self, loc: Location) {
        self.do_fov();

        if self.is_player() {
            if loc.terrain().is_exit() {
                action::next_level();
            }
        }
    }

// FOV and map memory //////////////////////////////////////////////////

    fn has_map_memory(self) -> bool {
        world::with(|w| w.map_memories().get(self).is_some())
    }

    fn do_fov(self) {
        let range = 12u;
        if let Some(loc) = self.location() {
            if self.has_map_memory() {
                let seen: Vec<Location> = Fov::new(
                    |pt| (loc + pt).blocks_sight(), range)
                    .map(|pt| loc + pt)
                    .collect();
                world::with_mut(|w| {
                    if let Some(ref mut mm) = w.map_memories_mut().get(self) {
                        mm.seen.clear();
                        mm.seen.extend(seen.clone().into_iter());
                        mm.remembered.extend(seen.iter().map(|&x| x));
                    } else {
                        panic!("Couldn't bind map memory");
                    }
                });
            }
        }
    }

    pub fn forget_map(self) {
        if self.has_map_memory() {
            world::with_mut(|w| {
                if let Some(ref mut mm) = w.map_memories_mut().get(self) {
                    mm.seen.clear();
                    mm.remembered.clear();
                } else {
                    panic!("Couldn't bind map memory");
                }
            });
        }
    }
}
<commit_msg>Better hostility relation<commit_after>use std::rand::Rng;
use calx::dijkstra::Dijkstra;
use calx::Rgb;
use world;
use location::{Location};
use dir6::Dir6;
use flags;
use components::{Intrinsic};
use components::{BrainState, Alignment};
use geom::HexGeom;
use spatial::Place;
use action;
use fov::Fov;
use rng;
use msg;

/// Game object handle.
#[deriving(Copy, PartialEq, Eq, Clone, Hash, Show, Decodable, Encodable)]
pub struct Entity(pub uint);

impl Entity {
    /// Place the entity in a location in the game world.
    pub fn place(self, loc: Location) {
        world::with_mut(|w| w.spatial.insert_at(self, loc));
        self.on_move_to(loc);
    }

    /// Remove the entity from all the game world systems. THE ENTITY VALUE
    /// WILL BE INVALID FROM HERE ON AND USING IT WILL LEAD TO BUGS. THE
    /// CALLER IS RESPONSIBLE FOR ENSUING THAT AN ENTITY WILL NOT BE
    /// USED FROM ANYWHERE AFTER THE DELETE OPERATION.
    pub fn delete(self) {
        // COMPONENTS CHECKPOINT
        // This needs to call every component system.
        world::with_mut(|w| w.descs_mut().remove(self));
        world::with_mut(|w| w.map_memories_mut().remove(self));
        world::with_mut(|w| w.spawns_mut().remove(self));
        world::with_mut(|w| w.healths_mut().remove(self));
        world::with_mut(|w| w.brains_mut().remove(self));

        world::with_mut(|w| w.spatial.remove(self));

        world::with_mut(|w| w.ecs.delete(self));
    }

    pub fn blocks_walk(self) -> bool { self.is_mob() }

    pub fn name(self) -> String {
        world::with(|w|
            match w.descs().get(self) {
                Some(desc) => desc.name.clone(),
                None => "".to_string()
            }
        )
    }

    pub fn get_icon(self) -> Option<(uint, Rgb)> {
        world::with(|w|
            if let Some(desc) = w.descs().get(self) {
                Some((desc.icon, desc.color))
            } else {
                None
            }
        )
    }

    /// Create a cloned entity that uses the current entity as a parent
    /// prototype. Components not defined in the clone entity will be read
    /// from the current entity.
    pub fn clone_at(self, loc: Location) -> Entity {
        let ret = world::with_mut(|w| { w.ecs.new_entity(Some(self)) });
        ret.place(loc);
        ret
    }

// Spatial methods /////////////////////////////////////////////////////

    pub fn can_enter(self, loc: Location) -> bool {
        if self.is_mob() && loc.has_mobs() { return false; }
        if loc.terrain().is_door() && !self.has_intrinsic(Intrinsic::Hands) {
            // Can't open doors without hands.
            return false;
        }
        if loc.blocks_walk() { return false; }
        true
    }

    /// Return whether the entity can move in a direction.
    pub fn can_step(self, dir: Dir6) -> bool {
        let place = world::with(|w| w.spatial.get(self));
        if let Some(Place::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            return self.can_enter(new_loc);
        }
        return false;
    }

    /// Try to move the entity in direction.
    pub fn step(self, dir: Dir6) {
        let place = world::with(|w| w.spatial.get(self));
        if let Some(Place::At(loc)) = place {
            let new_loc = loc + dir.to_v2();
            if self.can_enter(new_loc) {
                world::with_mut(|w| w.spatial.insert_at(self, new_loc));
                self.on_move_to(new_loc);
            }
        }
    }

    pub fn location(self) -> Option<Location> {
        match world::with(|w| w.spatial.get(self)) {
            Some(Place::At(loc)) => Some(loc),
            Some(Place::In(e)) => e.location(),
            _ => None
        }
    }

    pub fn distance_from(self, other: Entity) -> Option<int> {
        if let (Some(loc1), Some(loc2)) = (self.location(), other.location()) {
            loc1.distance_from(loc2)
        } else {
            None
        }
    }

// Damage and lifetime /////////////////////////////////////////////////

    pub fn damage(self, amount: int) {
        if amount == 0 { return; }
        let max_hp = self.max_hp();

        let (_amount, kill) = world::with_mut(|w| {
            let health = w.healths_mut().get(self).expect("no health");
            health.wounds += amount;
            (amount, health.wounds >= max_hp)
        });

        msg::push_msg(::Msg::Damage(self));
        if kill {
            self.kill();
        }
    }

    /// Do any game logic stuff related to this entity dying violently before
    /// deleting it.
    pub fn kill(self) {
        msgln!("{} dies.", self.name());
        msg::push_msg(::Msg::Gib(self.location().expect("no location")));
        self.delete();
    }

// Mob methods /////////////////////////////////////////////////////////

    pub fn is_mob(self) -> bool {
        world::with(|w| w.brains().get(self).is_some()) && self.location().is_some()
    }

    /// Return whether this mob is the player avatar.
    pub fn is_player(self) -> bool {
        self.brain_state() == Some(BrainState::PlayerControl) && self.location().is_some()
    }

    /*
    pub fn has_status(self, status: Status) -> bool {
        world::with(|w| {
            if let Some(&mob) = w.mobs().get(self) {
                return mob.has_status(status);
            }
            return false;
        })
    }

    pub fn add_status(self, status: Status) {
        if !self.is_mob() { return; }
        world::with_mut(|w| w.mobs_mut().get(self).expect("no mob").add_status(status));
        assert!(self.has_status(status));
    }

    pub fn remove_status(self, status: Status) {
        if !self.is_mob() { return; }
        world::with_mut(|w| w.mobs_mut().get(self).expect("no mob").remove_status(status));
        assert!(!self.has_status(status));
    }
    */

    pub fn has_intrinsic(self, intrinsic: Intrinsic) -> bool {
        world::with(|w|
            if let Some(stat) = w.mob_stats().get(self) {
                stat.intrinsics & intrinsic as u32 != 0
            } else {
                false
            })
    }

    /// Return whether this entity is an awake mob.
    pub fn is_active(self) -> bool {
        match self.brain_state() {
            Some(BrainState::Asleep) => false,
            Some(_) => true,
            _ => false
        }
    }

    /// Return if the entity is a mob that should get an update this frame
    /// based on its speed properties. Does not check for status effects like
    /// sleep that might prevent actual action.
    pub fn ticks_this_frame(self) -> bool {
        if self.is_player() { return true; }
        if !self.is_mob() { return false; }

        let tick = flags::get_tick();
        // Go through a cycle of 5 phases to get 4 possible speeds.
        // System idea from Jeff Lait.
        let phase = tick % 5;
        match phase {
            0 => return true,
            1 => return self.has_intrinsic(Intrinsic::Fast),
            2 => return true,
            3 => return false,//self.has_status(Status::Quick),
            4 => return !self.has_intrinsic(Intrinsic::Slow)
                        /*&& !self.has_status(Status::Slow)*/,
            _ => panic!("Invalid action phase"),
        }
    }

    /// Return whether the entity is a mob that will act this frame.
    pub fn acts_this_frame(self) -> bool {
        if !self.is_active() { return false; }
        return self.ticks_this_frame();
    }

    /// Return whether the entity is an awake non-player mob and should be
    /// animated with a bob.
    pub fn is_bobbing(self) -> bool {
        self.is_active() && !self.is_player()
    }

    pub fn melee(self, dir: Dir6) {
        let loc = self.location().expect("no location") + dir.to_v2();
        if let Some(e) = loc.mob_at() {
            // Attack power
            let p = self.power_level();
            if p == 0 {
                // No fight capacity.
                return;
            }

            // Every five points of power is one certain hit.
            let full = p / 5;
            // The fractional points are one probabilistic hit.
            let partial = (p % 5) as f64 / 5.0;

            let damage = full + if rng::p(partial) { 1 } else { 0 };
            e.damage(damage)
        }
    }

    pub fn power_level(self) -> int {
        world::with(|w|
            if let Some(stat) = w.mob_stats().get(self) { stat.power }
            else { 0 })
    }

    pub fn hp(self) -> int {
        self.max_hp() - world::with(|w|
            if let Some(health) = w.healths().get(self) {
                health.wounds
            } else {
                0
            })
    }

    pub fn max_hp(self) -> int {
        self.power_level()
    }

// AI methods /////////////////////////////////////////////////////////

    /// Top-level method called each frame to update the entity.
    pub fn update(self) {
        if self.is_mob() && !self.is_player() && self.ticks_this_frame() {
            self.mob_ai();
        }
    }

    fn brain_state(self) -> Option<BrainState> {
        world::with(|w| w.brains().get(self).map(|b| b.state))
    }

    fn set_brain_state(self, brain_state: BrainState) {
        world::with_mut(|w| w.brains_mut().get(self).expect("no brains").state = brain_state );
    }

    fn alignment(self) -> Option<Alignment> {
        world::with(|w| w.brains().get(self).map(|b| b.alignment))
    }

    fn wake_up(self) {
        if self.brain_state() == Some(BrainState::Asleep) {
            self.set_brain_state(BrainState::Hunting);
        }
    }

    /// AI routine for autonomous mobs.
    fn mob_ai(self) {
        assert!(self.is_mob());
        assert!(!self.is_player());
        assert!(self.ticks_this_frame());

        if self.brain_state() == Some(BrainState::Asleep) {
            if let Some(p) = action::player() {
                // TODO: Line-of-sight, stealth concerns, other enemies than
                // player etc.
                if let Some(d) = p.distance_from(self) {
                    if d < 6 {
                        self.wake_up();
                    }
                }
            }

            return;
        }

        if let Some(p) = action::player() {
            let loc = self.location().expect("no location");

            let vec_to_enemy = loc.v2_at(p.location().expect("no location"));
            if let Some(v) = vec_to_enemy {
                if v.hex_dist() == 1 {
                    // Melee range, hit.
                    self.melee(Dir6::from_v2(v));
                } else {
                    // Walk towards.
                    let pathing_depth = 16;
                    let pathing = Dijkstra::new(
                        vec![p.location().expect("no location")], |&loc| !loc.blocks_walk(),
                        pathing_depth);

                    let steps = pathing.sorted_neighbors(&loc);
                    if steps.len() > 0 {
                        self.step(loc.dir6_towards(steps[0]).expect("No loc pair orientation"));
                    } else {
                        self.step(rng::with(|ref mut rng| rng.gen::<Dir6>()));
                        // TODO: Fall asleep if things get boring.
                    }
                }
            }
        }
    }

    /// Return whether this thing wants to fight the other thing.
    fn is_hostile_to(self, other: Entity) -> bool {
        match (self.alignment(), other.alignment()) {
            (Some(Alignment::Chaotic), Some(_)) => true,
            (Some(_), Some(Alignment::Chaotic)) => true,
            (Some(Alignment::Evil), Some(Alignment::Good)) => true,
            (Some(Alignment::Good), Some(Alignment::Evil)) => true,
            _ => false,
        }
    }

    /// Return whether the mob has hostiles in its immediate vicinity.
    pub fn is_threatened(self) -> bool {
        // XXX: Expensive.
        let range = 12u;
        let loc = self.location().expect("no location");
        let seen: Vec<Location> = Fov::new(
            |pt| (loc + pt).blocks_sight(), range)
            .map(|pt| loc + pt)
            .collect();
        for loc in seen.iter() {
            if let Some(m) = loc.mob_at() {
                if m.is_hostile_to(self) {
                    return true;
                }
            }
        }
        false
    }

// Callbacks ///////////////////////////////////////////////////////////

    /// Called after the entity is moved to a new location.
    pub fn on_move_to(self, loc: Location) {
        self.do_fov();

        if self.is_player() {
            if loc.terrain().is_exit() {
                action::next_level();
            }
        }
    }

// FOV and map memory //////////////////////////////////////////////////

    fn has_map_memory(self) -> bool {
        world::with(|w| w.map_memories().get(self).is_some())
    }

    fn do_fov(self) {
        let range = 12u;
        if let Some(loc) = self.location() {
            if self.has_map_memory() {
                let seen: Vec<Location> = Fov::new(
                    |pt| (loc + pt).blocks_sight(), range)
                    .map(|pt| loc + pt)
                    .collect();
                world::with_mut(|w| {
                    if let Some(ref mut mm) = w.map_memories_mut().get(self) {
                        mm.seen.clear();
                        mm.seen.extend(seen.clone().into_iter());
                        mm.remembered.extend(seen.iter().map(|&x| x));
                    } else {
                        panic!("Couldn't bind map memory");
                    }
                });
            }
        }
    }

    pub fn forget_map(self) {
        if self.has_map_memory() {
            world::with_mut(|w| {
                if let Some(ref mut mm) = w.map_memories_mut().get(self) {
                    mm.seen.clear();
                    mm.remembered.clear();
                } else {
                    panic!("Couldn't bind map memory");
                }
            });
        }
    }
}
<|endoftext|>


<commit_before>//! CORS handling utility functions

use std::{fmt, ops};
use hosts::{Host, Port};
use matcher::{Matcher, Pattern};

/// Origin Protocol
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum OriginProtocol {
	/// Http protocol
	Http,
	/// Https protocol
	Https,
	/// Custom protocol
	Custom(String),
}

/// Request Origin
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Origin {
	protocol: OriginProtocol,
	host: Host,
	as_string: String,
	matcher: Matcher,
}

impl<T: AsRef<str>> From<T> for Origin {
	fn from(string: T) -> Self {
		Origin::parse(string.as_ref())
	}
}

impl Origin {
	fn with_host(protocol: OriginProtocol, host: Host) -> Self {
		let string = Self::to_string(&protocol, &host);
		let matcher = Matcher::new(&string);

		Origin {
			protocol: protocol,
			host: host,
			as_string: string,
			matcher: matcher,
		}
	}

	/// Creates new origin given protocol, hostname and port parts.
	/// Pre-processes input data if necessary.
	pub fn new<T: Into<Port>>(protocol: OriginProtocol, host: &str, port: T) -> Self {
		Self::with_host(protocol, Host::new(host, port))
	}

	/// Attempts to parse given string as a `Origin`.
	/// NOTE: This method always succeeds and falls back to sensible defaults.
	pub fn parse(data: &str) -> Self {
		let mut it = data.split("://");
		let proto = it.next().expect("split always returns non-empty iterator.");
		let hostname = it.next();

		let (proto, hostname) = match hostname {
			None => (None, proto),
			Some(hostname) => (Some(proto), hostname),
		};

		let proto = proto.map(str::to_lowercase);
		let hostname = Host::parse(hostname);

		let protocol = match proto {
			None => OriginProtocol::Http,
			Some(ref p) if p == "http" => OriginProtocol::Http,
			Some(ref p) if p == "https" => OriginProtocol::Https,
			Some(other) => OriginProtocol::Custom(other),
		};

		Origin::with_host(protocol, hostname)
	}

	fn to_string(protocol: &OriginProtocol, host: &Host) -> String {
		format!(
			"{}://{}",
			match *protocol {
				OriginProtocol::Http => "http",
				OriginProtocol::Https => "https",
				OriginProtocol::Custom(ref protocol) => protocol,
			},
			&**host,
		)
	}
}

impl Pattern for Origin {
	fn matches<T: AsRef<str>>(&self, other: T) -> bool {
		self.matcher.matches(other)
	}
}

impl ops::Deref for Origin {
	type Target = str;
	fn deref(&self) -> &Self::Target {
		&self.as_string
	}
}

/// Origins allowed to access
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessControlAllowOrigin {
	/// Specific hostname
	Value(Origin),
	/// null-origin (file:///, sandboxed iframe)
	Null,
	/// Any non-null origin
	Any,
}

impl fmt::Display for AccessControlAllowOrigin {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", match *self {
			AccessControlAllowOrigin::Any => "*",
			AccessControlAllowOrigin::Null => "null",
			AccessControlAllowOrigin::Value(ref val) => val,
		})
	}
}

impl<T: Into<String>> From<T> for AccessControlAllowOrigin {
	fn from(s: T) -> AccessControlAllowOrigin {
		match s.into().as_str() {
			"all" | "*" | "any" => AccessControlAllowOrigin::Any,
			"null" => AccessControlAllowOrigin::Null,
			origin => AccessControlAllowOrigin::Value(origin.into()),
		}
	}
}

/// CORS Header Result.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CorsHeader<T = AccessControlAllowOrigin> {
	/// CORS header was not required. Origin is not present in the request.
	NotRequired,
	/// CORS header is not returned, Origin is not allowed to access the resource.
	Invalid,
	/// CORS header to include in the response. Origin is allowed to access the resource.
	Ok(T),
}

impl<T> CorsHeader<T> {
	/// Maps `Ok` variant of `CorsHeader`.
	pub fn map<F, O>(self, f: F) -> CorsHeader<O> where
		F: FnOnce(T) -> O,
	{
		use self::CorsHeader::*;

		match self {
			NotRequired => NotRequired,
			Invalid => Invalid,
			Ok(val) => Ok(f(val)),
		}
	}
}

impl<T> Into<Option<T>> for CorsHeader<T> {
	fn into(self) -> Option<T> {
		use self::CorsHeader::*;

		match self {
			NotRequired | Invalid => None,
			Ok(header) => Some(header),
		}
	}
}

/// Returns correct CORS header (if any) given list of allowed origins and current origin.
pub fn get_cors_header(origin: Option<&str>, host: Option<&str>, allowed: &Option<Vec<AccessControlAllowOrigin>>) -> CorsHeader {
	match origin {
		None => CorsHeader::NotRequired,
		Some(ref origin) => {
			if let Some(host) = host {
				// Request initiated from the same server.
				if origin.ends_with(host) {
					// Additional check
					let origin = Origin::parse(origin);
					if &*origin.host == host {
						return CorsHeader::NotRequired;
					}
				}
			}

			match allowed.as_ref() {
				None => CorsHeader::Ok(AccessControlAllowOrigin::Value(Origin::parse(origin))),
				Some(ref allowed) if *origin == "null" => {
					allowed.iter().find(|cors| **cors == AccessControlAllowOrigin::Null).cloned()
						.map(CorsHeader::Ok)
						.unwrap_or(CorsHeader::Invalid)
				},
				Some(ref allowed) => {
					allowed.iter().find(|cors| {
						match **cors {
							AccessControlAllowOrigin::Any => true,
							AccessControlAllowOrigin::Value(ref val) if val.matches(origin) => true,
							_ => false
						}
					})
					.map(|_| AccessControlAllowOrigin::Value(Origin::parse(origin)))
					.map(CorsHeader::Ok).unwrap_or(CorsHeader::Invalid)
				},
			}
		},
	}
}

#[cfg(test)]
mod tests {
	use hosts::Host;
	use super::{get_cors_header, CorsHeader, AccessControlAllowOrigin, Origin, OriginProtocol};

	#[test]
	fn should_parse_origin() {
		use self::OriginProtocol::*;

		assert_eq!(Origin::parse("http://parity.io"), Origin::new(Http, "parity.io", None));
		assert_eq!(Origin::parse("https://parity.io:8443"), Origin::new(Https, "parity.io", Some(8443)));
		assert_eq!(Origin::parse("chrome-extension://124.0.0.1"), Origin::new(Custom("chrome-extension".into()), "124.0.0.1", None));
		assert_eq!(Origin::parse("parity.io/somepath"), Origin::new(Http, "parity.io", None));
		assert_eq!(Origin::parse("127.0.0.1:8545/somepath"), Origin::new(Http, "127.0.0.1", Some(8545)));
	}

	#[test]
	fn should_not_allow_partially_matching_origin() {
		// given
		let origin1 = Origin::parse("http://subdomain.somedomain.io");
		let origin2 = Origin::parse("http://somedomain.io:8080");
		let host = Host::parse("http://somedomain.io");

		let origin1 = Some(&*origin1);
		let origin2 = Some(&*origin2);
		let host = Some(&*host);

		// when
		let res1 = get_cors_header(origin1, host, &Some(vec![]));
		let res2 = get_cors_header(origin2, host, &Some(vec![]));

		// then
		assert_eq!(res1, CorsHeader::Invalid);
		assert_eq!(res2, CorsHeader::Invalid);
	}

	#[test]
	fn should_allow_origins_that_matches_hosts() {
		// given
		let origin = Origin::parse("http://127.0.0.1:8080");
		let host = Host::parse("http://127.0.0.1:8080");

		let origin = Some(&*origin);
		let host = Some(&*host);

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_when_there_are_no_cors_domains_and_no_origin() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_domain_when_all_are_allowed() {
		// given
		let origin = Some("parity.io");
		let host = None;

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::Ok("parity.io".into()));
	}

	#[test]
	fn should_return_none_for_empty_origin() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_for_empty_list() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(origin, host, &Some(Vec::new()));

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_for_not_matching_origin() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::Invalid);
	}

	#[test]
	fn should_return_specific_origin_if_we_allow_any() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(origin, host, &Some(vec![AccessControlAllowOrigin::Any]));

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
	}

	#[test]
	fn should_return_none_if_origin_is_not_defined() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Null]),
		);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_null_if_origin_is_null() {
		// given
		let origin = Some("null".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Null]),
		);

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Null));
	}

	#[test]
	fn should_return_specific_origin_if_there_is_a_match() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into()), AccessControlAllowOrigin::Value("http://parity.io".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
	}

	#[test]
	fn should_support_wildcards() {
		// given
		let origin1 = Some("http://parity.io".into());
		let origin2 = Some("http://parity.iot".into());
		let origin3 = Some("chrome-extension://test".into());
		let host = None;
		let allowed = Some(vec![
		   AccessControlAllowOrigin::Value("http://*.io".into()),
		   AccessControlAllowOrigin::Value("chrome-extension://*".into())
		]);

		// when
		let res1 = get_cors_header(origin1, host, &allowed);
		let res2 = get_cors_header(origin2, host, &allowed);
		let res3 = get_cors_header(origin3, host, &allowed);

		// then
		assert_eq!(res1, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
		assert_eq!(res2, CorsHeader::Invalid);
		assert_eq!(res3, CorsHeader::Ok(AccessControlAllowOrigin::Value("chrome-extension://test".into())));
	}
}
<commit_msg>Adding Hash to Origin. (#154)<commit_after>//! CORS handling utility functions

use std::{fmt, ops};
use hosts::{Host, Port};
use matcher::{Matcher, Pattern};

/// Origin Protocol
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum OriginProtocol {
	/// Http protocol
	Http,
	/// Https protocol
	Https,
	/// Custom protocol
	Custom(String),
}

/// Request Origin
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct Origin {
	protocol: OriginProtocol,
	host: Host,
	as_string: String,
	matcher: Matcher,
}

impl<T: AsRef<str>> From<T> for Origin {
	fn from(string: T) -> Self {
		Origin::parse(string.as_ref())
	}
}

impl Origin {
	fn with_host(protocol: OriginProtocol, host: Host) -> Self {
		let string = Self::to_string(&protocol, &host);
		let matcher = Matcher::new(&string);

		Origin {
			protocol: protocol,
			host: host,
			as_string: string,
			matcher: matcher,
		}
	}

	/// Creates new origin given protocol, hostname and port parts.
	/// Pre-processes input data if necessary.
	pub fn new<T: Into<Port>>(protocol: OriginProtocol, host: &str, port: T) -> Self {
		Self::with_host(protocol, Host::new(host, port))
	}

	/// Attempts to parse given string as a `Origin`.
	/// NOTE: This method always succeeds and falls back to sensible defaults.
	pub fn parse(data: &str) -> Self {
		let mut it = data.split("://");
		let proto = it.next().expect("split always returns non-empty iterator.");
		let hostname = it.next();

		let (proto, hostname) = match hostname {
			None => (None, proto),
			Some(hostname) => (Some(proto), hostname),
		};

		let proto = proto.map(str::to_lowercase);
		let hostname = Host::parse(hostname);

		let protocol = match proto {
			None => OriginProtocol::Http,
			Some(ref p) if p == "http" => OriginProtocol::Http,
			Some(ref p) if p == "https" => OriginProtocol::Https,
			Some(other) => OriginProtocol::Custom(other),
		};

		Origin::with_host(protocol, hostname)
	}

	fn to_string(protocol: &OriginProtocol, host: &Host) -> String {
		format!(
			"{}://{}",
			match *protocol {
				OriginProtocol::Http => "http",
				OriginProtocol::Https => "https",
				OriginProtocol::Custom(ref protocol) => protocol,
			},
			&**host,
		)
	}
}

impl Pattern for Origin {
	fn matches<T: AsRef<str>>(&self, other: T) -> bool {
		self.matcher.matches(other)
	}
}

impl ops::Deref for Origin {
	type Target = str;
	fn deref(&self) -> &Self::Target {
		&self.as_string
	}
}

/// Origins allowed to access
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessControlAllowOrigin {
	/// Specific hostname
	Value(Origin),
	/// null-origin (file:///, sandboxed iframe)
	Null,
	/// Any non-null origin
	Any,
}

impl fmt::Display for AccessControlAllowOrigin {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		write!(f, "{}", match *self {
			AccessControlAllowOrigin::Any => "*",
			AccessControlAllowOrigin::Null => "null",
			AccessControlAllowOrigin::Value(ref val) => val,
		})
	}
}

impl<T: Into<String>> From<T> for AccessControlAllowOrigin {
	fn from(s: T) -> AccessControlAllowOrigin {
		match s.into().as_str() {
			"all" | "*" | "any" => AccessControlAllowOrigin::Any,
			"null" => AccessControlAllowOrigin::Null,
			origin => AccessControlAllowOrigin::Value(origin.into()),
		}
	}
}

/// CORS Header Result.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CorsHeader<T = AccessControlAllowOrigin> {
	/// CORS header was not required. Origin is not present in the request.
	NotRequired,
	/// CORS header is not returned, Origin is not allowed to access the resource.
	Invalid,
	/// CORS header to include in the response. Origin is allowed to access the resource.
	Ok(T),
}

impl<T> CorsHeader<T> {
	/// Maps `Ok` variant of `CorsHeader`.
	pub fn map<F, O>(self, f: F) -> CorsHeader<O> where
		F: FnOnce(T) -> O,
	{
		use self::CorsHeader::*;

		match self {
			NotRequired => NotRequired,
			Invalid => Invalid,
			Ok(val) => Ok(f(val)),
		}
	}
}

impl<T> Into<Option<T>> for CorsHeader<T> {
	fn into(self) -> Option<T> {
		use self::CorsHeader::*;

		match self {
			NotRequired | Invalid => None,
			Ok(header) => Some(header),
		}
	}
}

/// Returns correct CORS header (if any) given list of allowed origins and current origin.
pub fn get_cors_header(origin: Option<&str>, host: Option<&str>, allowed: &Option<Vec<AccessControlAllowOrigin>>) -> CorsHeader {
	match origin {
		None => CorsHeader::NotRequired,
		Some(ref origin) => {
			if let Some(host) = host {
				// Request initiated from the same server.
				if origin.ends_with(host) {
					// Additional check
					let origin = Origin::parse(origin);
					if &*origin.host == host {
						return CorsHeader::NotRequired;
					}
				}
			}

			match allowed.as_ref() {
				None => CorsHeader::Ok(AccessControlAllowOrigin::Value(Origin::parse(origin))),
				Some(ref allowed) if *origin == "null" => {
					allowed.iter().find(|cors| **cors == AccessControlAllowOrigin::Null).cloned()
						.map(CorsHeader::Ok)
						.unwrap_or(CorsHeader::Invalid)
				},
				Some(ref allowed) => {
					allowed.iter().find(|cors| {
						match **cors {
							AccessControlAllowOrigin::Any => true,
							AccessControlAllowOrigin::Value(ref val) if val.matches(origin) => true,
							_ => false
						}
					})
					.map(|_| AccessControlAllowOrigin::Value(Origin::parse(origin)))
					.map(CorsHeader::Ok).unwrap_or(CorsHeader::Invalid)
				},
			}
		},
	}
}

#[cfg(test)]
mod tests {
	use hosts::Host;
	use super::{get_cors_header, CorsHeader, AccessControlAllowOrigin, Origin, OriginProtocol};

	#[test]
	fn should_parse_origin() {
		use self::OriginProtocol::*;

		assert_eq!(Origin::parse("http://parity.io"), Origin::new(Http, "parity.io", None));
		assert_eq!(Origin::parse("https://parity.io:8443"), Origin::new(Https, "parity.io", Some(8443)));
		assert_eq!(Origin::parse("chrome-extension://124.0.0.1"), Origin::new(Custom("chrome-extension".into()), "124.0.0.1", None));
		assert_eq!(Origin::parse("parity.io/somepath"), Origin::new(Http, "parity.io", None));
		assert_eq!(Origin::parse("127.0.0.1:8545/somepath"), Origin::new(Http, "127.0.0.1", Some(8545)));
	}

	#[test]
	fn should_not_allow_partially_matching_origin() {
		// given
		let origin1 = Origin::parse("http://subdomain.somedomain.io");
		let origin2 = Origin::parse("http://somedomain.io:8080");
		let host = Host::parse("http://somedomain.io");

		let origin1 = Some(&*origin1);
		let origin2 = Some(&*origin2);
		let host = Some(&*host);

		// when
		let res1 = get_cors_header(origin1, host, &Some(vec![]));
		let res2 = get_cors_header(origin2, host, &Some(vec![]));

		// then
		assert_eq!(res1, CorsHeader::Invalid);
		assert_eq!(res2, CorsHeader::Invalid);
	}

	#[test]
	fn should_allow_origins_that_matches_hosts() {
		// given
		let origin = Origin::parse("http://127.0.0.1:8080");
		let host = Host::parse("http://127.0.0.1:8080");

		let origin = Some(&*origin);
		let host = Some(&*host);

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_when_there_are_no_cors_domains_and_no_origin() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_domain_when_all_are_allowed() {
		// given
		let origin = Some("parity.io");
		let host = None;

		// when
		let res = get_cors_header(origin, host, &None);

		// then
		assert_eq!(res, CorsHeader::Ok("parity.io".into()));
	}

	#[test]
	fn should_return_none_for_empty_origin() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_for_empty_list() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(origin, host, &Some(Vec::new()));

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_none_for_not_matching_origin() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::Invalid);
	}

	#[test]
	fn should_return_specific_origin_if_we_allow_any() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(origin, host, &Some(vec![AccessControlAllowOrigin::Any]));

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
	}

	#[test]
	fn should_return_none_if_origin_is_not_defined() {
		// given
		let origin = None;
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Null]),
		);

		// then
		assert_eq!(res, CorsHeader::NotRequired);
	}

	#[test]
	fn should_return_null_if_origin_is_null() {
		// given
		let origin = Some("null".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Null]),
		);

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Null));
	}

	#[test]
	fn should_return_specific_origin_if_there_is_a_match() {
		// given
		let origin = Some("http://parity.io".into());
		let host = None;

		// when
		let res = get_cors_header(
			origin,
			host,
			&Some(vec![AccessControlAllowOrigin::Value("http://ethereum.org".into()), AccessControlAllowOrigin::Value("http://parity.io".into())]),
		);

		// then
		assert_eq!(res, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
	}

	#[test]
	fn should_support_wildcards() {
		// given
		let origin1 = Some("http://parity.io".into());
		let origin2 = Some("http://parity.iot".into());
		let origin3 = Some("chrome-extension://test".into());
		let host = None;
		let allowed = Some(vec![
		   AccessControlAllowOrigin::Value("http://*.io".into()),
		   AccessControlAllowOrigin::Value("chrome-extension://*".into())
		]);

		// when
		let res1 = get_cors_header(origin1, host, &allowed);
		let res2 = get_cors_header(origin2, host, &allowed);
		let res3 = get_cors_header(origin3, host, &allowed);

		// then
		assert_eq!(res1, CorsHeader::Ok(AccessControlAllowOrigin::Value("http://parity.io".into())));
		assert_eq!(res2, CorsHeader::Invalid);
		assert_eq!(res3, CorsHeader::Ok(AccessControlAllowOrigin::Value("chrome-extension://test".into())));
	}
}
<|endoftext|>


<commit_before>use rustc_serialize::{Encodable, Decodable, Encoder, Decoder};
use std::collections::HashMap;
use toml::encode_str;

use yxorp::messages::{Command,HttpFront,TlsFront,Instance};

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyInstance {
  ip_address: String,
  port:       u16,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, HttpProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct TlsProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  cert_path:  String,
  key_path:   String,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct TlsProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, TlsProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq)]
pub enum ConfigState {
  Http(HttpProxy),
  Tls(TlsProxy),
}

impl ConfigState {
  pub fn handle_command(&mut self, command: &Command) {
    match *self {
      ConfigState::Http(ref mut state) => state.handle_command(command),
      ConfigState::Tls(ref mut state)  => state.handle_command(command),
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    match *self {
      ConfigState::Http(ref state) => state.generate_commands(),
      ConfigState::Tls(ref state)  => state.generate_commands(),
    }
  }
}

impl Encodable for ConfigState {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    match *self {
      ConfigState::Http(ref state) => state.encode(e),
      ConfigState::Tls(ref state)  => state.encode(e),
    }
  }
}

impl HttpProxy {
  pub fn new(ip: String, port: u16) -> HttpProxy {
    HttpProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  pub fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddHttpFront(ref front) => {
        let f = HttpProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveHttpFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.iter() {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el.clone());
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    let mut v = Vec::new();
    for (app_id, front) in &self.fronts {
      v.push(Command::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
        port:       front.port.clone(),
      }));
      for instance in front.instances.iter() {
        v.push(Command::AddInstance(Instance {
          app_id:     app_id.clone(),
          ip_address: instance.ip_address.clone(),
          port:       instance.port.clone(),
        }));
      }
    }

    v
  }
}

impl TlsProxy {
  pub fn new(ip: String, port: u16) -> TlsProxy {
    TlsProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  pub fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddTlsFront(ref front) => {
        let f = TlsProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          cert_path:  front.cert_path.clone(),
          key_path:   front.key_path.clone(),
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveTlsFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.iter() {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el.clone());
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    let mut v = Vec::new();
    for (app_id, front) in &self.fronts {
      v.push(Command::AddTlsFront(TlsFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
        port:       front.port.clone(),
        cert_path:  front.cert_path.clone(),
        key_path:   front.key_path.clone(),
      }));
      for instance in front.instances.iter() {
        v.push(Command::AddInstance(Instance {
          app_id:     app_id.clone(),
          ip_address: instance.ip_address.clone(),
          port:       instance.port.clone(),
        }));
      }
    }

    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use yxorp::messages::{Command,HttpFront,Instance};
  use rustc_serialize::Encodable;
  use toml::decode_str;

  #[test]
  fn serialize() {
    let mut state = HttpProxy::new(String::from("127.0.0.1"), 80);
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/"), port: 8080 }));
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc"), port: 80 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_command(&Command::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }
}
<commit_msg>remove unused imports<commit_after>use rustc_serialize::{Encodable, Encoder, Decoder};
use std::collections::HashMap;

use yxorp::messages::{Command,HttpFront,TlsFront,Instance};

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyInstance {
  ip_address: String,
  port:       u16,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, HttpProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct TlsProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  cert_path:  String,
  key_path:   String,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct TlsProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, TlsProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq)]
pub enum ConfigState {
  Http(HttpProxy),
  Tls(TlsProxy),
}

impl ConfigState {
  pub fn handle_command(&mut self, command: &Command) {
    match *self {
      ConfigState::Http(ref mut state) => state.handle_command(command),
      ConfigState::Tls(ref mut state)  => state.handle_command(command),
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    match *self {
      ConfigState::Http(ref state) => state.generate_commands(),
      ConfigState::Tls(ref state)  => state.generate_commands(),
    }
  }
}

impl Encodable for ConfigState {
  fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
    match *self {
      ConfigState::Http(ref state) => state.encode(e),
      ConfigState::Tls(ref state)  => state.encode(e),
    }
  }
}

impl HttpProxy {
  pub fn new(ip: String, port: u16) -> HttpProxy {
    HttpProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  pub fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddHttpFront(ref front) => {
        let f = HttpProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveHttpFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.iter() {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el.clone());
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    let mut v = Vec::new();
    for (app_id, front) in &self.fronts {
      v.push(Command::AddHttpFront(HttpFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
        port:       front.port.clone(),
      }));
      for instance in front.instances.iter() {
        v.push(Command::AddInstance(Instance {
          app_id:     app_id.clone(),
          ip_address: instance.ip_address.clone(),
          port:       instance.port.clone(),
        }));
      }
    }

    v
  }
}

impl TlsProxy {
  pub fn new(ip: String, port: u16) -> TlsProxy {
    TlsProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  pub fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddTlsFront(ref front) => {
        let f = TlsProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          cert_path:  front.cert_path.clone(),
          key_path:   front.key_path.clone(),
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveTlsFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.iter() {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el.clone());
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  pub fn generate_commands(&self) -> Vec<Command> {
    let mut v = Vec::new();
    for (app_id, front) in &self.fronts {
      v.push(Command::AddTlsFront(TlsFront {
        app_id:     app_id.clone(),
        hostname:   front.hostname.clone(),
        path_begin: front.path_begin.clone(),
        port:       front.port.clone(),
        cert_path:  front.cert_path.clone(),
        key_path:   front.key_path.clone(),
      }));
      for instance in front.instances.iter() {
        v.push(Command::AddInstance(Instance {
          app_id:     app_id.clone(),
          ip_address: instance.ip_address.clone(),
          port:       instance.port.clone(),
        }));
      }
    }

    v
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use yxorp::messages::{Command,HttpFront,Instance};

  #[test]
  fn serialize() {
    let mut state = HttpProxy::new(String::from("127.0.0.1"), 80);
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/"), port: 8080 }));
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc"), port: 80 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_command(&Command::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    /*
    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    */
    //assert!(false);
  }
}
<|endoftext|>


<commit_before>use std::collections::hash_map;
use std::collections::HashMap;
use std::sync::Arc;

use result;
use server::handler::ServerHandler;
use server::handler::ServerHandlerContext;
use server::req::ServerRequest;
use solicit::header::Headers;
use ServerResponse;

#[derive(Default)]
struct Node {
    service: Option<Arc<ServerHandler>>,
    children: HashMap<String, Node>,
}

impl Node {
    fn add_service(&mut self, path: &str, service: Arc<ServerHandler>) {
        match split_path(path) {
            None => {
                self.service = Some(service);
            }
            Some((first, rem)) => {
                let node = match self.children.entry(first.to_owned()) {
                    hash_map::Entry::Occupied(e) => e.into_mut(),
                    hash_map::Entry::Vacant(e) => e.insert(Node {
                        service: None,
                        children: HashMap::new(),
                    }),
                };
                node.add_service(rem, service);
            }
        }
    }

    fn remove_service(&mut self, path: &str) -> Option<Arc<ServerHandler>> {
        match split_path(path) {
            None => self.service.take(),
            Some((first, rem)) => match self.children.get_mut(first) {
                Some(child) => child.remove_service(rem),
                None => None,
            },
        }
    }

    fn find_service(&self, path: &str) -> Option<&ServerHandler> {
        if let Some((first, rem)) = split_path(path) {
            if let Some(node) = self.children.get(first) {
                if let Some(service) = node.find_service(rem) {
                    return Some(service);
                }
            }
        }

        self.service.as_ref().map(|a| a.as_ref())
    }
}

fn split_path(mut path: &str) -> Option<(&str, &str)> {
    path = path.trim_start_matches('/');

    if path.is_empty() {
        None
    } else {
        let slash = path.find('/');
        match slash {
            Some(slash) => Some((&path[..slash], &path[slash + 1..])),
            None => Some((path, "")),
        }
    }
}

#[test]
fn test_split_path() {
    assert_eq!(None, split_path(""));
    assert_eq!(None, split_path("/"));
    assert_eq!(Some(("first", "")), split_path("first"));
    assert_eq!(Some(("first", "")), split_path("first/"));
    assert_eq!(Some(("first", "")), split_path("/first"));
    assert_eq!(Some(("first", "")), split_path("/first/"));
    assert_eq!(Some(("first", "second")), split_path("/first/second"));
    assert_eq!(Some(("first", "second/")), split_path("/first/second/"));
    assert_eq!(
        Some(("first", "second/third")),
        split_path("/first/second/third")
    );
    assert_eq!(
        Some(("first", "second/third/")),
        split_path("/first/second/third/")
    );
}

/// Convient implementation of `ServerHandler` which allows delegation to
/// multiple `ServerHandler` implementations provided by user.
#[derive(Default)]
pub struct ServerHandlerPaths {
    root: Node,
}

impl ServerHandlerPaths {
    /// Create a new `Service` implementation which returns `404`
    /// on all requests by default.
    pub fn new() -> ServerHandlerPaths {
        Default::default()
    }

    /// Register a service for given path.
    ///
    /// ```
    /// # use std::sync::Arc;
    /// # use httpbis::*;
    /// # use httpbis;
    ///
    /// struct Root {}
    /// struct Files {}
    ///
    /// impl ServerHandler for Root {
    ///     fn start_request(&self, _context: ServerHandlerContext, _req: ServerRequest, mut resp: ServerResponse) -> httpbis::Result<()> {
    ///         resp.send_found_200_plain_text("This is root page")?;
    ///         Ok(())
    ///     }
    /// }
    ///
    /// impl ServerHandler for Files {
    ///     fn start_request(&self, _context: ServerHandlerContext, _req: ServerRequest, mut resp: ServerResponse) -> httpbis::Result<()> {
    ///         resp.send_found_200_plain_text("This is files")?;
    ///         Ok(())
    ///     }
    /// }
    ///
    /// let mut server = ServerBuilder::new_plain();
    /// server.service.set_service("/", Arc::new(Root{}));
    /// server.service.set_service("/files", Arc::new(Files{}));
    /// ```
    pub fn set_service(&mut self, path: &str, service: Arc<ServerHandler>) {
        assert!(path.starts_with("/"));
        self.root.add_service(path, service);
    }

    pub fn set_service_fn<F>(&mut self, path: &str, service: F)
    where
        F: Fn(ServerHandlerContext, ServerRequest, ServerResponse) -> result::Result<()>
            + Send
            + Sync
            + 'static,
    {
        impl<
                F: Fn(ServerHandlerContext, ServerRequest, ServerResponse) -> result::Result<()>
                    + Send
                    + Sync
                    + 'static,
            > ServerHandler for F
        {
            fn start_request(
                &self,
                context: ServerHandlerContext,
                req: ServerRequest,
                resp: ServerResponse,
            ) -> result::Result<()> {
                self(context, req, resp)
            }
        }

        self.set_service(path, Arc::new(service))
    }

    pub fn remove_service(&mut self, path: &str) -> Option<Arc<ServerHandler>> {
        assert!(path.starts_with("/"));
        self.root.remove_service(path)
    }

    fn find_service(&self, path: &str) -> Option<&ServerHandler> {
        self.root.find_service(path)
    }
}

impl ServerHandler for ServerHandlerPaths {
    fn start_request(
        &self,
        context: ServerHandlerContext,
        req: ServerRequest,
        mut resp: ServerResponse,
    ) -> result::Result<()> {
        if let Some(service) = self.find_service(req.headers.path()) {
            debug!("invoking user callback for path {}", req.headers.path());
            service.start_request(context, req, resp)
        } else {
            debug!("serving 404 for path {}", req.headers.path());
            drop(resp.send_headers(Headers::not_found_404()));
            drop(resp.close());
            Ok(())
        }
    }
}
<commit_msg>Which callback invoked? Increase logging level to info<commit_after>use std::collections::hash_map;
use std::collections::HashMap;
use std::sync::Arc;

use result;
use server::handler::ServerHandler;
use server::handler::ServerHandlerContext;
use server::req::ServerRequest;
use solicit::header::Headers;
use ServerResponse;

#[derive(Default)]
struct Node {
    service: Option<Arc<ServerHandler>>,
    children: HashMap<String, Node>,
}

impl Node {
    fn add_service(&mut self, path: &str, service: Arc<ServerHandler>) {
        match split_path(path) {
            None => {
                self.service = Some(service);
            }
            Some((first, rem)) => {
                let node = match self.children.entry(first.to_owned()) {
                    hash_map::Entry::Occupied(e) => e.into_mut(),
                    hash_map::Entry::Vacant(e) => e.insert(Node {
                        service: None,
                        children: HashMap::new(),
                    }),
                };
                node.add_service(rem, service);
            }
        }
    }

    fn remove_service(&mut self, path: &str) -> Option<Arc<ServerHandler>> {
        match split_path(path) {
            None => self.service.take(),
            Some((first, rem)) => match self.children.get_mut(first) {
                Some(child) => child.remove_service(rem),
                None => None,
            },
        }
    }

    fn find_service(&self, path: &str) -> Option<&ServerHandler> {
        if let Some((first, rem)) = split_path(path) {
            if let Some(node) = self.children.get(first) {
                if let Some(service) = node.find_service(rem) {
                    return Some(service);
                }
            }
        }

        self.service.as_ref().map(|a| a.as_ref())
    }
}

fn split_path(mut path: &str) -> Option<(&str, &str)> {
    path = path.trim_start_matches('/');

    if path.is_empty() {
        None
    } else {
        let slash = path.find('/');
        match slash {
            Some(slash) => Some((&path[..slash], &path[slash + 1..])),
            None => Some((path, "")),
        }
    }
}

#[test]
fn test_split_path() {
    assert_eq!(None, split_path(""));
    assert_eq!(None, split_path("/"));
    assert_eq!(Some(("first", "")), split_path("first"));
    assert_eq!(Some(("first", "")), split_path("first/"));
    assert_eq!(Some(("first", "")), split_path("/first"));
    assert_eq!(Some(("first", "")), split_path("/first/"));
    assert_eq!(Some(("first", "second")), split_path("/first/second"));
    assert_eq!(Some(("first", "second/")), split_path("/first/second/"));
    assert_eq!(
        Some(("first", "second/third")),
        split_path("/first/second/third")
    );
    assert_eq!(
        Some(("first", "second/third/")),
        split_path("/first/second/third/")
    );
}

/// Convient implementation of `ServerHandler` which allows delegation to
/// multiple `ServerHandler` implementations provided by user.
#[derive(Default)]
pub struct ServerHandlerPaths {
    root: Node,
}

impl ServerHandlerPaths {
    /// Create a new `Service` implementation which returns `404`
    /// on all requests by default.
    pub fn new() -> ServerHandlerPaths {
        Default::default()
    }

    /// Register a service for given path.
    ///
    /// ```
    /// # use std::sync::Arc;
    /// # use httpbis::*;
    /// # use httpbis;
    ///
    /// struct Root {}
    /// struct Files {}
    ///
    /// impl ServerHandler for Root {
    ///     fn start_request(&self, _context: ServerHandlerContext, _req: ServerRequest, mut resp: ServerResponse) -> httpbis::Result<()> {
    ///         resp.send_found_200_plain_text("This is root page")?;
    ///         Ok(())
    ///     }
    /// }
    ///
    /// impl ServerHandler for Files {
    ///     fn start_request(&self, _context: ServerHandlerContext, _req: ServerRequest, mut resp: ServerResponse) -> httpbis::Result<()> {
    ///         resp.send_found_200_plain_text("This is files")?;
    ///         Ok(())
    ///     }
    /// }
    ///
    /// let mut server = ServerBuilder::new_plain();
    /// server.service.set_service("/", Arc::new(Root{}));
    /// server.service.set_service("/files", Arc::new(Files{}));
    /// ```
    pub fn set_service(&mut self, path: &str, service: Arc<ServerHandler>) {
        assert!(path.starts_with("/"));
        self.root.add_service(path, service);
    }

    pub fn set_service_fn<F>(&mut self, path: &str, service: F)
    where
        F: Fn(ServerHandlerContext, ServerRequest, ServerResponse) -> result::Result<()>
            + Send
            + Sync
            + 'static,
    {
        impl<
                F: Fn(ServerHandlerContext, ServerRequest, ServerResponse) -> result::Result<()>
                    + Send
                    + Sync
                    + 'static,
            > ServerHandler for F
        {
            fn start_request(
                &self,
                context: ServerHandlerContext,
                req: ServerRequest,
                resp: ServerResponse,
            ) -> result::Result<()> {
                self(context, req, resp)
            }
        }

        self.set_service(path, Arc::new(service))
    }

    pub fn remove_service(&mut self, path: &str) -> Option<Arc<ServerHandler>> {
        assert!(path.starts_with("/"));
        self.root.remove_service(path)
    }

    fn find_service(&self, path: &str) -> Option<&ServerHandler> {
        self.root.find_service(path)
    }
}

impl ServerHandler for ServerHandlerPaths {
    fn start_request(
        &self,
        context: ServerHandlerContext,
        req: ServerRequest,
        mut resp: ServerResponse,
    ) -> result::Result<()> {
        if let Some(service) = self.find_service(req.headers.path()) {
            info!("invoking user callback for path {}", req.headers.path());
            service.start_request(context, req, resp)
        } else {
            info!("serving 404 for path {}", req.headers.path());
            drop(resp.send_headers(Headers::not_found_404()));
            drop(resp.close());
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>ls: remove path strip<commit_after><|endoftext|>


<commit_before>use specs::prelude::*;

use sdl2::rect::Rect;

use crate::components::{Position, Text, Sprites};
use crate::core::{
    Action, CombatData, CombatState, DisplayStr, InputContext, Map, Tile, TileType, UserInput,
    WorldPos, TextureMap,
};
use crate::ui::{ClickArea, Scene, ScreenPos, ScreenText, ScreenSprite, TILE_WIDTH};

pub type SystemData<'a> = (
    ReadStorage<'a, Position>,
    ReadStorage<'a, Sprites>,
    ReadStorage<'a, Text>,
    Read<'a, Map>,
    Read<'a, TextureMap>,
);

pub fn render(
    viewport: &Rect,
    scroll_offset: (i32, i32),
    game: &CombatData,
) -> (Scene, Vec<ClickArea>) {
    let (pos, sprites, texts, map, texture_map): SystemData = game.world.system_data();

    let mut scene = Scene {
        sprites: Vec::new(),
        texts: vec![],
        background: (252, 246, 218),
    };

    render_map(&mut scene, scroll_offset, game, &map, &texture_map);
    render_character(&mut scene, scroll_offset, &pos, &sprites);
    render_texts(&mut scene, scroll_offset, &pos, &texts);

    let default_action = get_default_action(&game);

    (
        scene,
        vec![ClickArea {
            clipping_area: viewport.clone(),
            action: Box::new(move |screen_pos| {
                let screen_pos = ScreenPos(screen_pos.0 - TILE_WIDTH as i32 / 2, screen_pos.1);
                let clicked_pos = screen_pos.to_world_pos(scroll_offset);

                if let Some((wp, action, cost)) = &default_action {
                    if clicked_pos.0.floor() == wp.0.floor()
                        && clicked_pos.1.floor() == wp.1.floor()
                    {
                        return UserInput::SelectAction((action.clone(), *cost));
                    }
                }

                UserInput::SelectWorldPos(clicked_pos)
            }),
        }],
    )
}

fn render_character<'a>(
    scene: &mut Scene,
    offset: (i32, i32),
    positions: &ReadStorage<Position>,
    visuals: &ReadStorage<Sprites>,
) {
    for (pos, sprite_cmp) in (positions, visuals).join() {
        let p = ScreenPos::from_world_pos(pos.0, offset);

        for sprite in sprite_cmp.sample(p) {
            scene.sprites.push(sprite);
        }
    }
}

fn render_texts<'a>(
    scene: &mut Scene,
    offset: (i32, i32),
    positions: &ReadStorage<Position>,
    texts: &ReadStorage<Text>,
) {
    for (pos, text) in (positions, texts).join() {
        let ScreenPos(mut x, mut y) = ScreenPos::from_world_pos(pos.0, offset);
        if let Some((dx, dy)) = text.offset {
            x += dx;
            y += dy;
        }

        scene.texts.push(ScreenText {
            font: text.font,
            text: DisplayStr::new(text.txt.clone()),
            pos: ScreenPos(x, y),
            color: text.color,
            background: text.background,
            padding: text.padding,
            border: text.border,
            min_width: 0,
            max_width: u32::max_value(),
        });
    }
}

fn render_map(
    scene: &mut Scene,
    offset: (i32, i32),
    game: &CombatData,
    map: &Map,
    texture_map: &TextureMap,
) {
    for tile in map.tiles() {
        if let Some(sprite_config) = map_tile_to_texture(tile).and_then(|tn| texture_map.get(&tn)) {
            let tile_pos = tile.to_world_pos();
            let p = ScreenPos::from_world_pos(tile_pos, offset);

            scene.sprites.push(ScreenSprite(p, sprite_config.sample(0)));
            // scene.sprites.push(sprite_config.into_screen_sprite(p, 0));
        }
    }

    if let Some(wp) = get_focus_pos(&game.state) {
        if let Some(sprite_config) = texture_map.get("selected") {
            let p = ScreenPos::from_world_pos(wp, offset);

            scene.sprites.push(ScreenSprite(p, sprite_config.sample(0)));
            // scene.sprites.push(sprite_config.into_screen_sprite(p, 0));
        }
    }
}

fn map_tile_to_texture(t: Tile) -> Option<String> {
    match t.tile_type() {
        TileType::Floor => Some(String::from("floor")),
        _ => None,
    }
}

fn get_focus_pos<'a>(game_state: &CombatState) -> Option<WorldPos> {
    match game_state {
        CombatState::WaitForUserAction(_, Some(InputContext::SelectedArea(p, _, _))) => Some(*p),
        _ => None,
    }
}

fn get_default_action(game: &CombatData) -> Option<(WorldPos, Action, u8)> {
    match &game.state {
        CombatState::WaitForUserAction(_, Some(InputContext::SelectedArea(pos, _, actions_at))) => {
            actions_at
                .iter()
                .cloned()
                .next()
                .map(|(action, cost)| (*pos, action, cost))
        }

        _ => None,
    }
}
<commit_msg>Highlight movent path<commit_after>use std::iter::once;

use specs::prelude::*;

use sdl2::rect::Rect;

use crate::components::{Position, Text, Sprites};
use crate::core::{
    Action, CombatData, CombatState, DisplayStr, InputContext, Map, Tile, TileType, UserInput,
    WorldPos, TextureMap,
};
use crate::ui::{ClickArea, Scene, ScreenPos, ScreenText, ScreenSprite, TILE_WIDTH};

pub type SystemData<'a> = (
    ReadStorage<'a, Position>,
    ReadStorage<'a, Sprites>,
    ReadStorage<'a, Text>,
    Read<'a, Map>,
    Read<'a, TextureMap>,
);

pub fn render(
    viewport: &Rect,
    scroll_offset: (i32, i32),
    game: &CombatData,
) -> (Scene, Vec<ClickArea>) {
    let (pos, sprites, texts, map, texture_map): SystemData = game.world.system_data();

    let mut scene = Scene {
        sprites: Vec::new(),
        texts: vec![],
        background: (252, 246, 218),
    };

    let default_action = get_default_action(&game);

    render_map(&mut scene, scroll_offset, &default_action, &map, &texture_map);
    render_character(&mut scene, scroll_offset, &pos, &sprites);
    render_texts(&mut scene, scroll_offset, &pos, &texts);

    (
        scene,
        vec![ClickArea {
            clipping_area: viewport.clone(),
            action: Box::new(move |screen_pos| {
                let screen_pos = ScreenPos(screen_pos.0 - TILE_WIDTH as i32 / 2, screen_pos.1);
                let clicked_pos = screen_pos.to_world_pos(scroll_offset);

                if let Some((wp, action, cost)) = &default_action {
                    if clicked_pos.0.floor() == wp.0.floor()
                        && clicked_pos.1.floor() == wp.1.floor()
                    {
                        return UserInput::SelectAction((action.clone(), *cost));
                    }
                }

                UserInput::SelectWorldPos(clicked_pos)
            }),
        }],
    )
}

fn render_character<'a>(
    scene: &mut Scene,
    offset: (i32, i32),
    positions: &ReadStorage<Position>,
    visuals: &ReadStorage<Sprites>,
) {
    for (pos, sprite_cmp) in (positions, visuals).join() {
        let p = ScreenPos::from_world_pos(pos.0, offset);

        for sprite in sprite_cmp.sample(p) {
            scene.sprites.push(sprite);
        }
    }
}

fn render_texts<'a>(
    scene: &mut Scene,
    offset: (i32, i32),
    positions: &ReadStorage<Position>,
    texts: &ReadStorage<Text>,
) {
    for (pos, text) in (positions, texts).join() {
        let ScreenPos(mut x, mut y) = ScreenPos::from_world_pos(pos.0, offset);
        if let Some((dx, dy)) = text.offset {
            x += dx;
            y += dy;
        }

        scene.texts.push(ScreenText {
            font: text.font,
            text: DisplayStr::new(text.txt.clone()),
            pos: ScreenPos(x, y),
            color: text.color,
            background: text.background,
            padding: text.padding,
            border: text.border,
            min_width: 0,
            max_width: u32::max_value(),
        });
    }
}

fn render_map(
    scene: &mut Scene,
    offset: (i32, i32),
    default_action: &Option<(WorldPos, Action, u8)>,
    map: &Map,
    texture_map: &TextureMap,
) {
    for tile in map.tiles() {
        if let Some(sprite_config) = map_tile_to_texture(tile).and_then(|tn| texture_map.get(&tn)) {
            let tile_pos = tile.to_world_pos();
            let p = ScreenPos::from_world_pos(tile_pos, offset);

            scene.sprites.push(ScreenSprite(p, sprite_config.sample(0)));
            // scene.sprites.push(sprite_config.into_screen_sprite(p, 0));
        }
    }

    for wp in get_highlighted_tiles(default_action) {
        if let Some(sprite_config) = texture_map.get("selected") {
            let p = ScreenPos::from_world_pos(wp, offset);

            scene.sprites.push(ScreenSprite(p, sprite_config.sample(0)));
        }
    }

    // if let Some(wp) = default_action {
    //     if let Some(sprite_config) = texture_map.get("selected") {
    //         let p = ScreenPos::from_world_pos(wp, offset);

    //         scene.sprites.push(ScreenSprite(p, sprite_config.sample(0)));
    //     }
    // }
}

fn map_tile_to_texture(t: Tile) -> Option<String> {
    match t.tile_type() {
        TileType::Floor => Some(String::from("floor")),
        _ => None,
    }
}

// fn get_focus_pos<'a>(game_state: &CombatState) -> Option<WorldPos> {
//     match game_state {
//         CombatState::WaitForUserAction(_, Some(InputContext::SelectedArea(p, _, _))) => Some(*p),
//         _ => None,
//     }
// }

fn get_default_action(game: &CombatData) -> Option<(WorldPos, Action, u8)> {
    match &game.state {
        CombatState::WaitForUserAction(_, Some(InputContext::SelectedArea(pos, _, actions_at))) => {
            actions_at
                .iter()
                .cloned()
                .next()
                .map(|(action, cost)| (*pos, action, cost))
        }

        _ => None,
    }
}

fn get_highlighted_tiles(
    default_action: &Option<(WorldPos, Action, u8)>
) -> Vec<WorldPos> {
    if let Some((pos, action, _)) = default_action {
        match action {
            Action::MoveTo(p) => once(*pos).chain(p.iter().map(|t| t.to_world_pos())).collect(),
            _ => vec!(*pos),
        }
    } else {
        vec!()
    }
}
<|endoftext|>


<commit_before>//! Context management
use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

pub use self::context::{Context, Status};
pub use self::list::ContextList;
pub use self::switch::switch;

/// Context struct
mod context;

/// Context list
mod list;

/// Context switch function
mod switch;

/// Event handling
pub mod event;

/// File struct - defines a scheme and a file number
pub mod file;

/// Memory struct - contains a set of pages for a context
pub mod memory;

/// Limit on number of contexts
pub const CONTEXT_MAX_CONTEXTS: usize = 65536;

/// Maximum context files
pub const CONTEXT_MAX_FILES: usize = 65536;

/// Contexts list
static CONTEXTS: Once<RwLock<ContextList>> = Once::new();

#[thread_local]
static CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn init() {
    let mut contexts = contexts_mut();
    let context_lock = contexts.new_context().expect("could not initialize first context");
    let mut context = context_lock.write();
    let mut fx = unsafe { Box::from_raw(::alloc::heap::allocate(512, 16) as *mut [u8; 512]) };
    for b in fx.iter_mut() {
        *b = 0;
    }

    context.arch.set_fx(fx.as_ptr() as usize);
    context.kfx = Some(fx);
    context.status = Status::Runnable;
    context.running = true;
    context.cpu_id = Some(::cpu_id());
    CONTEXT_ID.store(context.id, Ordering::SeqCst);
}

/// Initialize contexts, called if needed
fn init_contexts() -> RwLock<ContextList> {
    RwLock::new(ContextList::new())
}

/// Get the global schemes list, const
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).read()
}

/// Get the global schemes list, mutable
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).write()
}

pub fn context_id() -> usize {
    CONTEXT_ID.load(Ordering::SeqCst)
}
<commit_msg>increase process id range (#729)<commit_after>//! Context management
use alloc::boxed::Box;
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

pub use self::context::{Context, Status};
pub use self::list::ContextList;
pub use self::switch::switch;

/// Context struct
mod context;

/// Context list
mod list;

/// Context switch function
mod switch;

/// Event handling
pub mod event;

/// File struct - defines a scheme and a file number
pub mod file;

/// Memory struct - contains a set of pages for a context
pub mod memory;

/// Limit on number of contexts
pub const CONTEXT_MAX_CONTEXTS: usize = usize::max_value() - 1;

/// Maximum context files
pub const CONTEXT_MAX_FILES: usize = 65536;

/// Contexts list
static CONTEXTS: Once<RwLock<ContextList>> = Once::new();

#[thread_local]
static CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn init() {
    let mut contexts = contexts_mut();
    let context_lock = contexts.new_context().expect("could not initialize first context");
    let mut context = context_lock.write();
    let mut fx = unsafe { Box::from_raw(::alloc::heap::allocate(512, 16) as *mut [u8; 512]) };
    for b in fx.iter_mut() {
        *b = 0;
    }

    context.arch.set_fx(fx.as_ptr() as usize);
    context.kfx = Some(fx);
    context.status = Status::Runnable;
    context.running = true;
    context.cpu_id = Some(::cpu_id());
    CONTEXT_ID.store(context.id, Ordering::SeqCst);
}

/// Initialize contexts, called if needed
fn init_contexts() -> RwLock<ContextList> {
    RwLock::new(ContextList::new())
}

/// Get the global schemes list, const
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).read()
}

/// Get the global schemes list, mutable
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).write()
}

pub fn context_id() -> usize {
    CONTEXT_ID.load(Ordering::SeqCst)
}
<|endoftext|>