<commit_before>use calx_grid::Dir6;
use display;
use euclid::{Point2D, Rect, vec2};
use rand;
use scancode::Scancode;
use std::fs::File;
use std::io::prelude::*;
use vitral::{Context, FracPoint2D, FracSize2D, FracRect, Align};
use world::{Command, CommandResult, Event, ItemType, Location, Query, Slot, TerrainQuery, World, on_screen};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State {
    Main,
    Inventory(InventoryAction),
    Console,
    Aim(AimAction),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum InventoryAction {
    Drop,
    Equip,
    Use,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AimAction {
    Zap(Slot),
    // Maybe add intrinsic abilities not tied to a specific entity later
}

pub struct GameLoop {
    pub world: World,
    pub console: display::Console,
    state: State,
}

impl GameLoop {
    pub fn new(world: World) -> GameLoop {
        GameLoop {
            world,
            console: display::Console::default(),
            state: State::Main,
        }
    }

    /// Step command that turns into melee attack if an enemy is in the way.
    fn smart_step(&mut self, dir: Dir6) -> CommandResult {
        let player = self.world.player().ok_or(())?;
        let loc = self.world.location(player).ok_or(())?;
        let destination = loc + dir;

        if let Some(mob) = self.world.mob_at(destination) {
            if self.world.is_hostile_to(player, mob) {
                // Fight on!
                self.world.melee(dir)
            } else {
                // Do we want to do something smarter than walk into friendlies?
                // The world might treat this as a displace action so keep it like this for now.
                self.world.step(dir)
            }
        } else {
            self.world.step(dir)
        }
    }

    fn game_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Console;
                Ok(Vec::new())
            }
            Q => self.smart_step(Dir6::Northwest),
            W => self.smart_step(Dir6::North),
            E => self.smart_step(Dir6::Northeast),
            A => self.smart_step(Dir6::Southwest),
            S => self.smart_step(Dir6::South),
            D => self.smart_step(Dir6::Southeast),
            I => {
                self.state = State::Inventory(InventoryAction::Equip);
                Ok(Vec::new())
            }
            // XXX: Key mnemonics, bit awkward when D is taken by movement.
            B => {
                self.state = State::Inventory(InventoryAction::Drop);
                Ok(Vec::new())
            }
            U => {
                self.state = State::Inventory(InventoryAction::Use);
                Ok(Vec::new())
            }
            G => self.world.take(),
            F5 => {
                self.world
                    .save(&mut File::create("save.gam").unwrap())
                    .unwrap();
                Ok(Vec::new())
            }
            F9 => {
                let mut savefile = File::open("save.gam").unwrap();
                self.world = World::load(&mut savefile).unwrap();
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn aim_input(&mut self, slot: Slot, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Q => self.world.zap_item(slot, Dir6::Northwest),
            W => self.world.zap_item(slot, Dir6::North),
            E => self.world.zap_item(slot, Dir6::Northeast),
            A => self.world.zap_item(slot, Dir6::Southwest),
            S => self.world.zap_item(slot, Dir6::South),
            D => self.world.zap_item(slot, Dir6::Southeast),
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        for slot in SLOT_DATA.iter() {
            if scancode == slot.code {
                if let State::Inventory(action) = self.state {
                    let ret = self.inventory_action(slot.slot, action);
                    if ret.is_ok() {
                        return ret;
                    }
                }
            }
        }

        match scancode {
            Escape => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_action(&mut self, slot: Slot, action: InventoryAction) -> CommandResult {
        match action {
            InventoryAction::Drop => {
                let ret = self.world.drop(slot);
                // After succesful drop, go back to main state.
                if ret.is_ok() {
                    self.state = State::Main;
                }
                ret
            }
            // Can equip multiple items in one go, wait for ESC to return to main state.
            InventoryAction::Equip => self.world.equip(slot),
            InventoryAction::Use => {
                let player = self.world.player().ok_or(())?;

                if let Some(item) = self.world.entity_equipped(player, slot) {
                    match self.world.item_type(item) {
                        Some(ItemType::UntargetedUsable(_)) => {
                            return self.world.use_item(slot);
                        }
                        Some(ItemType::TargetedUsable(_)) => {
                            // If we need to aim, switch to aim state before calling world.
                            self.state = State::Aim(AimAction::Zap(slot));
                            return Ok(Vec::new());
                        }
                        _ => {}
                    }
                }
                return Err(());
            }
        }
    }

    fn console_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.state = State::Main;
                Ok(Vec::new())
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn dump(&mut self) { dump_map(&self.world); }

    /// Generate a new random cave map.
    fn cave(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::caves(
            &mut self.world,
            &mut rand::thread_rng(),
            Location::new(0, 0, 0),
            300,
        );
    }

    /// Generate a new random maze map.
    fn maze(&mut self, sparseness: usize) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::maze(&mut self.world, &mut rand::thread_rng(), sparseness);
    }

    /// Generate a new random rooms and corridors
    fn rooms(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::rooms(&mut self.world, &mut rand::thread_rng());
    }

    command_parser!{
        fn cave(&mut self);
        fn maze(&mut self, sparseness: usize);
        fn rooms(&mut self);

        fn dump(&mut self);
    }

    fn draw_inventory(&mut self, c: &mut display::Backend) -> Result<(), ()> {
        let player = self.world.player().ok_or(())?;

        // Start with hardcoded invetory data to test the UI logic.
        c.fill_rect(
            FracRect::new(FracPoint2D::new(0.0, 0.0), FracSize2D::new(1.0, 1.0)),
            [0.0, 0.0, 0.0, 0.99],
        );

        let mut letter_pos = Point2D::new(0.0, 0.0);
        let mut slot_name_pos = Point2D::new(20.0, 0.0);
        let mut item_name_pos = Point2D::new(80.0, 0.0);
        let text_color = [1.0, 1.0, 1.0, 1.0];

        for slot in SLOT_DATA.iter() {
            // TODO: Bounding box for these is a button...
            letter_pos = c.draw_text(
                letter_pos,
                Align::Left,
                text_color,
                &format!("{})", slot.key),
            );
            slot_name_pos = c.draw_text(slot_name_pos, Align::Left, text_color, slot.name);
            let item_name = if let Some(item) = self.world.entity_equipped(player, slot.slot) {
                self.world.entity_name(item)
            } else {
                "".to_string()
            };

            item_name_pos = c.draw_text(item_name_pos, Align::Left, text_color, &item_name);
        }

        Ok(())
    }

    pub fn draw(&mut self, context: &mut display::Backend, screen_area: &Rect<f32>) {
        let camera_loc = Location::new(0, 0, 0);
        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;

        view.draw(&self.world, context);

        match self.state {
            State::Inventory(_) => {
                let _ = self.draw_inventory(context);
            }
            State::Console => {
                let mut console_area = *screen_area;
                console_area.size.height /= 2.0;
                self.console.draw_large(context, &console_area);
            }
            _ => {
                let mut console_area = *screen_area;
                console_area.size.height /= 8.0;
                self.console.draw_small(context, &console_area);
            }
        }

        if let Some(scancode) = context.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            let ret = match self.state {
                State::Inventory(_) => self.inventory_input(scancode),
                State::Console => self.console_input(scancode),
                State::Aim(AimAction::Zap(slot)) => self.aim_input(slot, scancode),
                _ => self.game_input(scancode),
            };

            if let Ok(events) = ret {
                // Input event caused a successful world step and we got an event sequence out.
                // Convert events into UI display effects.
                for e in events {
                    match e {
                        Event::Msg(text) => {
                            let _ = writeln!(&mut self.console, "{}", text);
                        }
                    }
                }
            }
        }
    }
}

/// Print the world map as ASCII.
fn dump_map(world: &World) {
    for y in -21..21 {
        for x in -39..41 {
            if (x + y) % 2 != 0 {
                print!(" ");
                continue;
            }
            let pos = vec2((x + y) / 2, y);
            if on_screen(pos) {
                let t = world.terrain(Location::new(0, 0, 0) + pos);
                if t.is_open() {
                    print!(".");
                } else if t.is_door() {
                    print!("+");
                } else if t.is_wall() {
                    print!("#");
                } else {
                    print!("*");
                }
            } else {
                print!(" ");
            }
        }
        println!("");
    }
}

struct SlotData {
    key: char,
    code: Scancode,
    slot: Slot,
    name: &'static str,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', code: Scancode::Num1, slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', code: Scancode::Num2, slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', code: Scancode::Num3, slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', code: Scancode::Num4, slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', code: Scancode::Num5, slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', code: Scancode::Num6, slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', code: Scancode::Num7, slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', code: Scancode::Num8, slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', code: Scancode::A,    slot: Slot::Melee,      name: "Weapon" },
    SlotData { key: 'b', code: Scancode::B,    slot: Slot::Ranged,     name: "Ranged" },
    SlotData { key: 'c', code: Scancode::C,    slot: Slot::Head,       name: "Head" },
    SlotData { key: 'd', code: Scancode::D,    slot: Slot::Body,       name: "Body" },
    SlotData { key: 'e', code: Scancode::E,    slot: Slot::Feet,       name: "Feet" },
    SlotData { key: 'f', code: Scancode::F,    slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', code: Scancode::G,    slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', code: Scancode::H,    slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', code: Scancode::I,    slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', code: Scancode::J,    slot: Slot::InventoryJ, name: "" },
    SlotData { key: 'k', code: Scancode::K,    slot: Slot::InventoryK, name: "" },
    SlotData { key: 'l', code: Scancode::L,    slot: Slot::InventoryL, name: "" },
    SlotData { key: 'm', code: Scancode::M,    slot: Slot::InventoryM, name: "" },
    SlotData { key: 'n', code: Scancode::N,    slot: Slot::InventoryN, name: "" },
    SlotData { key: 'o', code: Scancode::O,    slot: Slot::InventoryO, name: "" },
    SlotData { key: 'p', code: Scancode::P,    slot: Slot::InventoryP, name: "" },
    SlotData { key: 'q', code: Scancode::Q,    slot: Slot::InventoryQ, name: "" },
    SlotData { key: 'r', code: Scancode::R,    slot: Slot::InventoryR, name: "" },
    SlotData { key: 's', code: Scancode::S,    slot: Slot::InventoryS, name: "" },
    SlotData { key: 't', code: Scancode::T,    slot: Slot::InventoryT, name: "" },
    SlotData { key: 'u', code: Scancode::U,    slot: Slot::InventoryU, name: "" },
    SlotData { key: 'v', code: Scancode::V,    slot: Slot::InventoryV, name: "" },
    SlotData { key: 'w', code: Scancode::W,    slot: Slot::InventoryW, name: "" },
    SlotData { key: 'x', code: Scancode::X,    slot: Slot::InventoryX, name: "" },
    SlotData { key: 'y', code: Scancode::Y,    slot: Slot::InventoryY, name: "" },
    SlotData { key: 'z', code: Scancode::Z,    slot: Slot::InventoryZ, name: "" },
];
<commit_msg>State transitions after ending inventory actions<commit_after>use calx_grid::Dir6;
use display;
use euclid::{Point2D, Rect, vec2};
use rand;
use scancode::Scancode;
use std::fs::File;
use std::io::prelude::*;
use vitral::{Context, FracPoint2D, FracSize2D, FracRect, Align};
use world::{Command, CommandResult, Event, ItemType, Location, Query, Slot, TerrainQuery, World, on_screen};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum State {
    Main,
    Inventory(InventoryAction),
    Console,
    Aim(AimAction),
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum InventoryAction {
    Drop,
    Equip,
    Use,
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum AimAction {
    Zap(Slot),
    // Maybe add intrinsic abilities not tied to a specific entity later
}

pub struct GameLoop {
    pub world: World,
    pub console: display::Console,
    state: State,
}

impl GameLoop {
    pub fn new(world: World) -> GameLoop {
        GameLoop {
            world,
            console: display::Console::default(),
            state: State::Main,
        }
    }

    /// Step command that turns into melee attack if an enemy is in the way.
    fn smart_step(&mut self, dir: Dir6) -> CommandResult {
        let player = self.world.player().ok_or(())?;
        let loc = self.world.location(player).ok_or(())?;
        let destination = loc + dir;

        if let Some(mob) = self.world.mob_at(destination) {
            if self.world.is_hostile_to(player, mob) {
                // Fight on!
                self.world.melee(dir)
            } else {
                // Do we want to do something smarter than walk into friendlies?
                // The world might treat this as a displace action so keep it like this for now.
                self.world.step(dir)
            }
        } else {
            self.world.step(dir)
        }
    }

    fn game_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.enter_state(State::Console);
                Ok(Vec::new())
            }
            Q => self.smart_step(Dir6::Northwest),
            W => self.smart_step(Dir6::North),
            E => self.smart_step(Dir6::Northeast),
            A => self.smart_step(Dir6::Southwest),
            S => self.smart_step(Dir6::South),
            D => self.smart_step(Dir6::Southeast),
            I => {
                self.enter_state(State::Inventory(InventoryAction::Equip));
                Ok(Vec::new())
            }
            // XXX: Key mnemonics, bit awkward when D is taken by movement.
            B => {
                self.enter_state(State::Inventory(InventoryAction::Drop));
                Ok(Vec::new())
            }
            U => {
                self.enter_state(State::Inventory(InventoryAction::Use));
                Ok(Vec::new())
            }
            G => self.world.take(),
            F5 => {
                self.world
                    .save(&mut File::create("save.gam").unwrap())
                    .unwrap();
                Ok(Vec::new())
            }
            F9 => {
                let mut savefile = File::open("save.gam").unwrap();
                self.world = World::load(&mut savefile).unwrap();
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn zap(&mut self, slot: Slot, dir: Dir6) -> CommandResult {
        let ret = self.world.zap_item(slot, dir)?;
        self.enter_state(State::Main);
        Ok(ret)
    }

    fn aim_input(&mut self, slot: Slot, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Q => self.zap(slot, Dir6::Northwest),
            W => self.zap(slot, Dir6::North),
            E => self.zap(slot, Dir6::Northeast),
            A => self.zap(slot, Dir6::Southwest),
            S => self.zap(slot, Dir6::South),
            D => self.zap(slot, Dir6::Southeast),
            Escape => {
                self.enter_state(State::Main);
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn inventory_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        for slot in SLOT_DATA.iter() {
            if scancode == slot.code {
                if let State::Inventory(action) = self.state {
                    let ret = self.inventory_action(slot.slot, action);
                    if ret.is_ok() {
                        return ret;
                    }
                }
            }
        }

        match scancode {
            Escape => {
                self.enter_state(State::Main);
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn enter_state(&mut self, new_state: State) {
        if self.state == new_state {
            return;
        }

        if let State::Aim(_) = new_state {
            let _ = writeln!(&mut self.console, "Press direction to aim, Esc to cancel");
        }

        self.state = new_state;
    }

    fn inventory_action(&mut self, slot: Slot, action: InventoryAction) -> CommandResult {
        match action {
            InventoryAction::Drop => {
                let ret = self.world.drop(slot);
                // After succesful drop, go back to main state.
                if ret.is_ok() {
                    self.enter_state(State::Main);
                }
                ret
            }
            // Can equip multiple items in one go, wait for ESC to return to main state.
            InventoryAction::Equip => self.world.equip(slot),
            InventoryAction::Use => {
                let player = self.world.player().ok_or(())?;

                if let Some(item) = self.world.entity_equipped(player, slot) {
                    match self.world.item_type(item) {
                        Some(ItemType::UntargetedUsable(_)) => {
                            let ret = self.world.use_item(slot)?;
                            self.enter_state(State::Main);
                            return Ok(ret);
                        }
                        Some(ItemType::TargetedUsable(_)) => {
                            // If we need to aim, switch to aim state before calling world.
                            self.enter_state(State::Aim(AimAction::Zap(slot)));
                            return Ok(Vec::new());
                        }
                        _ => {}
                    }
                }
                return Err(());
            }
        }
    }

    fn console_input(&mut self, scancode: Scancode) -> CommandResult {
        use scancode::Scancode::*;
        match scancode {
            Tab => {
                self.enter_state(State::Main);
                Ok(Vec::new())
            }
            Enter | PadEnter => {
                let input = self.console.get_input();
                let _ = writeln!(&mut self.console, "{}", input);
                if let Err(e) = self.parse(&input) {
                    let _ = writeln!(&mut self.console, "{}", e);
                }
                Ok(Vec::new())
            }
            _ => Ok(Vec::new()),
        }
    }

    fn dump(&mut self) { dump_map(&self.world); }

    /// Generate a new random cave map.
    fn cave(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::caves(
            &mut self.world,
            &mut rand::thread_rng(),
            Location::new(0, 0, 0),
            300,
        );
    }

    /// Generate a new random maze map.
    fn maze(&mut self, sparseness: usize) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::maze(&mut self.world, &mut rand::thread_rng(), sparseness);
    }

    /// Generate a new random rooms and corridors
    fn rooms(&mut self) {
        use world::mapgen;
        self.world = World::new(1);
        mapgen::rooms(&mut self.world, &mut rand::thread_rng());
    }

    command_parser!{
        fn cave(&mut self);
        fn maze(&mut self, sparseness: usize);
        fn rooms(&mut self);

        fn dump(&mut self);
    }

    fn draw_inventory(&mut self, c: &mut display::Backend) -> Result<(), ()> {
        let player = self.world.player().ok_or(())?;

        // Start with hardcoded invetory data to test the UI logic.
        c.fill_rect(
            FracRect::new(FracPoint2D::new(0.0, 0.0), FracSize2D::new(1.0, 1.0)),
            [0.0, 0.0, 0.0, 0.99],
        );

        let mut letter_pos = Point2D::new(0.0, 0.0);
        let mut slot_name_pos = Point2D::new(20.0, 0.0);
        let mut item_name_pos = Point2D::new(80.0, 0.0);
        let text_color = [1.0, 1.0, 1.0, 1.0];

        for slot in SLOT_DATA.iter() {
            // TODO: Bounding box for these is a button...
            letter_pos = c.draw_text(
                letter_pos,
                Align::Left,
                text_color,
                &format!("{})", slot.key),
            );
            slot_name_pos = c.draw_text(slot_name_pos, Align::Left, text_color, slot.name);
            let item_name = if let Some(item) = self.world.entity_equipped(player, slot.slot) {
                self.world.entity_name(item)
            } else {
                "".to_string()
            };

            item_name_pos = c.draw_text(item_name_pos, Align::Left, text_color, &item_name);
        }

        Ok(())
    }

    pub fn draw(&mut self, context: &mut display::Backend, screen_area: &Rect<f32>) {
        let camera_loc = Location::new(0, 0, 0);
        let mut view = display::WorldView::new(camera_loc, *screen_area);
        view.show_cursor = true;

        view.draw(&self.world, context);

        match self.state {
            State::Inventory(_) => {
                let _ = self.draw_inventory(context);
            }
            State::Console => {
                let mut console_area = *screen_area;
                console_area.size.height /= 2.0;
                self.console.draw_large(context, &console_area);
            }
            _ => {
                let mut console_area = *screen_area;
                console_area.size.height /= 8.0;
                self.console.draw_small(context, &console_area);
            }
        }

        if let Some(scancode) = context.poll_key().and_then(|k| Scancode::new(k.scancode)) {
            let ret = match self.state {
                State::Inventory(_) => self.inventory_input(scancode),
                State::Console => self.console_input(scancode),
                State::Aim(AimAction::Zap(slot)) => self.aim_input(slot, scancode),
                _ => self.game_input(scancode),
            };

            if let Ok(events) = ret {
                // Input event caused a successful world step and we got an event sequence out.
                // Convert events into UI display effects.
                for e in events {
                    match e {
                        Event::Msg(text) => {
                            let _ = writeln!(&mut self.console, "{}", text);
                        }
                    }
                }
            }
        }
    }
}

/// Print the world map as ASCII.
fn dump_map(world: &World) {
    for y in -21..21 {
        for x in -39..41 {
            if (x + y) % 2 != 0 {
                print!(" ");
                continue;
            }
            let pos = vec2((x + y) / 2, y);
            if on_screen(pos) {
                let t = world.terrain(Location::new(0, 0, 0) + pos);
                if t.is_open() {
                    print!(".");
                } else if t.is_door() {
                    print!("+");
                } else if t.is_wall() {
                    print!("#");
                } else {
                    print!("*");
                }
            } else {
                print!(" ");
            }
        }
        println!("");
    }
}

struct SlotData {
    key: char,
    code: Scancode,
    slot: Slot,
    name: &'static str,
}

#[cfg_attr(rustfmt, rustfmt_skip)]
static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', code: Scancode::Num1, slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', code: Scancode::Num2, slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', code: Scancode::Num3, slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', code: Scancode::Num4, slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', code: Scancode::Num5, slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', code: Scancode::Num6, slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', code: Scancode::Num7, slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', code: Scancode::Num8, slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', code: Scancode::A,    slot: Slot::Melee,      name: "Weapon" },
    SlotData { key: 'b', code: Scancode::B,    slot: Slot::Ranged,     name: "Ranged" },
    SlotData { key: 'c', code: Scancode::C,    slot: Slot::Head,       name: "Head" },
    SlotData { key: 'd', code: Scancode::D,    slot: Slot::Body,       name: "Body" },
    SlotData { key: 'e', code: Scancode::E,    slot: Slot::Feet,       name: "Feet" },
    SlotData { key: 'f', code: Scancode::F,    slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', code: Scancode::G,    slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', code: Scancode::H,    slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', code: Scancode::I,    slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', code: Scancode::J,    slot: Slot::InventoryJ, name: "" },
    SlotData { key: 'k', code: Scancode::K,    slot: Slot::InventoryK, name: "" },
    SlotData { key: 'l', code: Scancode::L,    slot: Slot::InventoryL, name: "" },
    SlotData { key: 'm', code: Scancode::M,    slot: Slot::InventoryM, name: "" },
    SlotData { key: 'n', code: Scancode::N,    slot: Slot::InventoryN, name: "" },
    SlotData { key: 'o', code: Scancode::O,    slot: Slot::InventoryO, name: "" },
    SlotData { key: 'p', code: Scancode::P,    slot: Slot::InventoryP, name: "" },
    SlotData { key: 'q', code: Scancode::Q,    slot: Slot::InventoryQ, name: "" },
    SlotData { key: 'r', code: Scancode::R,    slot: Slot::InventoryR, name: "" },
    SlotData { key: 's', code: Scancode::S,    slot: Slot::InventoryS, name: "" },
    SlotData { key: 't', code: Scancode::T,    slot: Slot::InventoryT, name: "" },
    SlotData { key: 'u', code: Scancode::U,    slot: Slot::InventoryU, name: "" },
    SlotData { key: 'v', code: Scancode::V,    slot: Slot::InventoryV, name: "" },
    SlotData { key: 'w', code: Scancode::W,    slot: Slot::InventoryW, name: "" },
    SlotData { key: 'x', code: Scancode::X,    slot: Slot::InventoryX, name: "" },
    SlotData { key: 'y', code: Scancode::Y,    slot: Slot::InventoryY, name: "" },
    SlotData { key: 'z', code: Scancode::Z,    slot: Slot::InventoryZ, name: "" },
];
<|endoftext|>


<commit_before>use super::number::Number;
use super::point::Point;

pub enum LineIntersection<T> {
   Point(Point<T>),
   Parallel,
   Overlapping,
}

#[derive(Debug)]
pub struct Line<T> {
   a: T,
   b: T,
   c: T
}

impl<T> Line<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      Line {
         a: a,
         b: b,
         c: c
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      self.a = a;
      self.b = b;
      self.c = c;
   }

   #[inline]
   fn calc_abc(x1: T, y1: T, x2: T, y2: T) -> (T, T, T) {
      let mut a = y2 - y1;
      let mut b = x1 - x2;
      let mut c = -(x1 * y2 - x2 * y1);

      let mut norm = (T::one() / (a * a + b * b)).sqrt();
      if a != T::zero() {
         if a < T::zero() {
            norm = -norm;
         }
      } else {
         if b < T::zero() {
            norm = -norm;
         }
      }

      a = a * norm;
      b = b * norm;
      c = c * norm;

      (a, b, c)
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection<T> {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == T::zero() {
         return LineIntersection::Overlapping;
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x, y))
   }
}

#[derive(Debug)]
pub struct LineSegment<T> {
   p1: Point<T>,
   p2: Point<T>,
   line: Line<T>
}

impl<T> LineSegment<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let p1 = Point::new(x1, y1);
      let p2 = Point::new(x2, y2);
      let line = Line::new(x1, y1, x2, y2);

      LineSegment {
         p1: p1,
         p2: p2,
         line: line
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      self.p1.x = x1;
      self.p1.y = y1;
      self.p2.x = x2;
      self.p2.y = y2;

      self.line.update(x1, y1, x2, y2);
   }

   #[inline]
   pub fn x1(&self) -> T {
      self.p1.x
   }

   #[inline]
   pub fn y1(&self) -> T {
      self.p1.y
   }

   #[inline]
   pub fn x2(&self) -> T {
      self.p2.x
   }

   #[inline]
   pub fn y2(&self) -> T {
      self.p2.y
   }

   #[inline]
   pub fn line(&self) -> &Line<T> {
      &self.line
   }
}

impl<T> Default for LineSegment<T> where T: Number {
   fn default() -> LineSegment<T> {
      LineSegment::new(T::zero(), T::zero(), T::zero(), T::zero())
   }
}

#[cfg(test)]
mod tests {
   use test::{Bencher, black_box};

   use super::*;

   #[test]
   fn test_segment_new_f64() {
      let ls = LineSegment::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ls.p1.x, 10_f64);
      assert_eq!(ls.p1.y, 20_f64);
      assert_eq!(ls.p2.x, -100_f64);
      assert_eq!(ls.p2.y, 0_f64);
   }

   #[test]
   fn test_segment_new_f32() {
      let ls = LineSegment::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ls.p1.x, 10_f32);
      assert_eq!(ls.p1.y, 20_f32);
      assert_eq!(ls.p2.x, -100_f32);
      assert_eq!(ls.p2.y, 0_f32);
   }

   #[test]
   fn test_line_new_f64() {
      let ln = Line::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ln.a, 0.1788854381999832_f64);
      assert_eq!(ln.b, -0.9838699100999075_f64);
      assert_eq!(ln.c, 17.88854381999832_f64);
   }

   #[test]
   fn test_line_new_f32() {
      let ln = Line::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ln.a, 0.1788854381999832_f32);
      assert_eq!(ln.b, -0.9838699100999075_f32);
      assert_eq!(ln.c, 17.88854381999832_f32);
   }

   #[test]
   fn test_vline_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 200_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 200_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 101_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 101_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_down_f64() {
      let ln = Line::new(
         50_f64, 101_f64,
         50_f64, 100_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_down_f32() {
      let ln = Line::new(
         50_f32, 101_f32,
         50_f32, 100_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         200_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         200_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         101_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         101_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_down_f64() {
      let ln = Line::new(
         101_f64, 50_f64,
         100_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_down_f32() {
      let ln = Line::new(
         101_f32, 50_f32,
         100_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[bench]
   fn bench_line_new_f64(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f64),
                  black_box(7_f64),
                  black_box(3_f64),
                  black_box(2_f64)
               )
            );
         }
      });
   }

   #[bench]
   fn bench_line_new_f32(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f32),
                  black_box(7_f32),
                  black_box(3_f32),
                  black_box(2_f32)
               )
            );
         }
      });
   }
}

<commit_msg>Tests for line normalization<commit_after>use super::number::Number;
use super::point::Point;

pub enum LineIntersection<T> {
   Point(Point<T>),
   Parallel,
   Overlapping,
}

#[derive(Debug)]
pub struct Line<T> {
   a: T,
   b: T,
   c: T
}

impl<T> Line<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      Line {
         a: a,
         b: b,
         c: c
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      let (a, b, c) = Self::calc_abc(x1, y1, x2, y2);
      self.a = a;
      self.b = b;
      self.c = c;
   }

   #[inline]
   fn calc_abc(x1: T, y1: T, x2: T, y2: T) -> (T, T, T) {
      let mut a = y2 - y1;
      let mut b = x1 - x2;
      let mut c = -(x1 * y2 - x2 * y1);

      let mut norm = (T::one() / (a * a + b * b)).sqrt();
      if a != T::zero() {
         if a < T::zero() {
            norm = -norm;
         }
      } else {
         if b < T::zero() {
            norm = -norm;
         }
      }

      a = a * norm;
      b = b * norm;
      c = c * norm;

      (a, b, c)
   }

   pub fn intersect(&self, line: &Self) -> LineIntersection<T> {
      let denominator = self.a * line.b - line.a * self.b;

      if denominator == T::zero() {
         return LineIntersection::Overlapping;
      }

      let x = (line.c * self.b - self.c * line.b) / denominator;
      let y = (line.a * self.c - self.a * line.c) / denominator;

      LineIntersection::Point(Point::new(x, y))
   }
}

#[derive(Debug)]
pub struct LineSegment<T> {
   p1: Point<T>,
   p2: Point<T>,
   line: Line<T>
}

impl<T> LineSegment<T> where T: Number {
   pub fn new(x1: T, y1: T, x2: T, y2: T) -> Self {
      let p1 = Point::new(x1, y1);
      let p2 = Point::new(x2, y2);
      let line = Line::new(x1, y1, x2, y2);

      LineSegment {
         p1: p1,
         p2: p2,
         line: line
      }
   }

   pub fn update(&mut self, x1: T, y1: T, x2: T, y2: T) {
      self.p1.x = x1;
      self.p1.y = y1;
      self.p2.x = x2;
      self.p2.y = y2;

      self.line.update(x1, y1, x2, y2);
   }

   #[inline]
   pub fn x1(&self) -> T {
      self.p1.x
   }

   #[inline]
   pub fn y1(&self) -> T {
      self.p1.y
   }

   #[inline]
   pub fn x2(&self) -> T {
      self.p2.x
   }

   #[inline]
   pub fn y2(&self) -> T {
      self.p2.y
   }

   #[inline]
   pub fn line(&self) -> &Line<T> {
      &self.line
   }
}

impl<T> Default for LineSegment<T> where T: Number {
   fn default() -> LineSegment<T> {
      LineSegment::new(T::zero(), T::zero(), T::zero(), T::zero())
   }
}

#[cfg(test)]
mod tests {
   use std::{f32, f64};
   use test::{Bencher, black_box};

   use super::*;

   #[test]
   fn test_segment_new_f64() {
      let ls = LineSegment::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ls.p1.x, 10_f64);
      assert_eq!(ls.p1.y, 20_f64);
      assert_eq!(ls.p2.x, -100_f64);
      assert_eq!(ls.p2.y, 0_f64);
   }

   #[test]
   fn test_segment_new_f32() {
      let ls = LineSegment::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ls.p1.x, 10_f32);
      assert_eq!(ls.p1.y, 20_f32);
      assert_eq!(ls.p2.x, -100_f32);
      assert_eq!(ls.p2.y, 0_f32);
   }

   #[test]
   fn test_line_new_f64() {
      let ln = Line::new(
         10_f64, 20_f64,
         -100_f64, 0_f64
      );
      assert_eq!(ln.a, 0.1788854381999832_f64);
      assert_eq!(ln.b, -0.9838699100999075_f64);
      assert_eq!(ln.c, 17.88854381999832_f64);

      assert!(f64::abs(1_f64 - (ln.a*ln.a+ln.b*ln.b)) <= f64::EPSILON);
   }

   #[test]
   fn test_line_new_f32() {
      let ln = Line::new(
         10_f32, 20_f32,
         -100_f32, 0_f32
      );
      assert_eq!(ln.a, 0.1788854381999832_f32);
      assert_eq!(ln.b, -0.9838699100999075_f32);
      assert_eq!(ln.c, 17.88854381999832_f32);

      assert!(f32::abs(1_f32 - (ln.a*ln.a+ln.b*ln.b)) <= f32::EPSILON);
   }

   #[test]
   fn test_vline_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 200_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 200_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_f64() {
      let ln = Line::new(
         50_f64, 100_f64,
         50_f64, 101_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_f32() {
      let ln = Line::new(
         50_f32, 100_f32,
         50_f32, 101_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_vline_one_down_f64() {
      let ln = Line::new(
         50_f64, 101_f64,
         50_f64, 100_f64
      );
      assert_eq!(ln.a, 1_f64);
      assert_eq!(ln.b, 0_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_vline_one_down_f32() {
      let ln = Line::new(
         50_f32, 101_f32,
         50_f32, 100_f32
      );
      assert_eq!(ln.a, 1_f32);
      assert_eq!(ln.b, 0_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         200_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         200_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_f64() {
      let ln = Line::new(
         100_f64, 50_f64,
         101_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_f32() {
      let ln = Line::new(
         100_f32, 50_f32,
         101_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[test]
   fn test_hline_one_down_f64() {
      let ln = Line::new(
         101_f64, 50_f64,
         100_f64, 50_f64
      );
      assert_eq!(ln.a, 0_f64);
      assert_eq!(ln.b, 1_f64);
      assert_eq!(ln.c, -50_f64);
   }

   #[test]
   fn test_hline_one_down_f32() {
      let ln = Line::new(
         101_f32, 50_f32,
         100_f32, 50_f32
      );
      assert_eq!(ln.a, 0_f32);
      assert_eq!(ln.b, 1_f32);
      assert_eq!(ln.c, -50_f32);
   }

   #[bench]
   fn bench_line_new_f64(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f64),
                  black_box(7_f64),
                  black_box(3_f64),
                  black_box(2_f64)
               )
            );
         }
      });
   }

   #[bench]
   fn bench_line_new_f32(b: &mut Bencher) {
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               Line::new(
                  black_box(5_f32),
                  black_box(7_f32),
                  black_box(3_f32),
                  black_box(2_f32)
               )
            );
         }
      });
   }
}

<|endoftext|>


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

use std::borrow::Cow;
use std::iter::repeat;

use syntax::codemap::{BytePos, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, last_line_width, mk_sp};
use visitor::FmtVisitor;

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.len() == 0
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| this.push_str(last_snippet))
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(&snippet));
            process_last_snippet(self, "", &snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        // The buffer already has a trailing newline.
        let offset = if last_line_width(&self.buffer) == 0 {
            0
        } else {
            1
        };
        let newline_upper_bound = self.config.blank_lines_upper_bound() + offset;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + offset;
        if newline_count > newline_upper_bound {
            newline_count = newline_upper_bound;
        } else if newline_count < newline_lower_bound {
            newline_count = newline_lower_bound;
        }
        let blank_lines: String = repeat('\n').take(newline_count).collect();
        self.push_str(&blank_lines);
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, &snippet, span, process_last_snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        big_diff: usize,
        subslice: &str,
        file_name: &str,
    ) -> bool {
        let last_char = big_snippet[..(offset + big_diff)]
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        let subslice_num_lines = count_newlines(subslice);
        let skip_this_range = !self.config.file_lines().intersects_range(
            file_name,
            status.cur_line,
            status.cur_line + subslice_num_lines,
        );

        if status.rewrite_next_comment && skip_this_range {
            status.rewrite_next_comment = false;
        }

        if status.rewrite_next_comment {
            let comment_indent = if fix_indent {
                if let Some('{') = last_char {
                    self.push_str("\n");
                }
                let indent_str = self.block_indent.to_string(self.config);
                self.push_str(&indent_str);
                self.block_indent
            } else {
                self.push_str(" ");
                Indent::from_width(self.config, last_line_width(&self.buffer))
            };
            let comment_width = ::std::cmp::min(
                self.config.comment_width(),
                self.config.max_width() - self.block_indent.width(),
            );
            let comment_shape = Shape::legacy(comment_width, comment_indent);
            let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
                .unwrap_or_else(|| String::from(subslice));
            self.push_str(&comment_str);

            status.last_wspace = None;
            status.line_start = offset + subslice.len();

            if let Some('/') = subslice.chars().nth(1) {
                // check that there are no contained block comments
                if !subslice
                    .split('\n')
                    .map(|s| s.trim_left())
                    .any(|s| s.len() >= 2 && &s[0..2] == "/*")
                {
                    // Add a newline after line comments
                    self.push_str("\n");
                }
            } else if status.line_start <= snippet.len() {
                // For other comments add a newline if there isn't one at the end already
                match snippet[status.line_start..].chars().next() {
                    Some('\n') | Some('\r') => (),
                    _ => self.push_str("\n"),
                }
            }

            status.cur_line += subslice_num_lines;
            true
        } else {
            status.rewrite_next_comment = false;
            false
        }
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        fn replace_chars<'a>(string: &'a str) -> Cow<'a, str> {
            if string.contains(char::is_whitespace) {
                Cow::from(
                    string
                        .chars()
                        .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
                        .collect::<String>(),
                )
            } else {
                Cow::from(string)
            }
        }

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => replace_chars(old_snippet),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            if let CodeCharKind::Comment = kind {
                if self.process_comment(
                    &mut status,
                    snippet,
                    big_snippet,
                    offset,
                    big_diff,
                    subslice,
                    file_name,
                ) {
                    continue;
                }
            }

            let newline_count = count_newlines(&subslice);
            if subslice.trim().is_empty() && newline_count > 0
                && self.config.file_lines().intersects_range(
                    file_name,
                    status.cur_line,
                    status.cur_line + newline_count,
                ) {
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.rewrite_next_comment = true;
                status.line_start = offset + newline_count;
            } else {
                for (mut i, c) in subslice.char_indices() {
                    i += offset;

                    if c == '\n' {
                        if !self.config
                            .file_lines()
                            .contains_line(file_name, status.cur_line)
                        {
                            status.last_wspace = None;
                        }

                        if let Some(lw) = status.last_wspace {
                            self.push_str(&snippet[status.line_start..lw]);
                            self.push_str("\n");
                        } else {
                            self.push_str(&snippet[status.line_start..i + 1]);
                        }

                        status.cur_line += 1;
                        status.line_start = i + 1;
                        status.last_wspace = None;
                        status.rewrite_next_comment = true;
                    } else if c.is_whitespace() {
                        if status.last_wspace.is_none() {
                            status.last_wspace = Some(i);
                        }
                    } else if c == ';' {
                        if status.last_wspace.is_some() {
                            status.line_start = i;
                        }

                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    } else {
                        status.rewrite_next_comment = true;
                        status.last_wspace = None;
                    }
                }

                let remaining = snippet[status.line_start..subslice.len() + offset].trim();
                if !remaining.is_empty() {
                    self.push_str(remaining);
                    status.line_start = subslice.len() + offset;
                    status.rewrite_next_comment = true;
                }
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }
}

struct SnippetStatus {
    line_start: usize,
    last_wspace: Option<usize>,
    rewrite_next_comment: bool,
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            rewrite_next_comment: true,
            cur_line,
        }
    }
}
<commit_msg>Refactor write_snippet_inner()<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::borrow::Cow;
use std::iter::repeat;

use syntax::codemap::{BytePos, Pos, Span};

use codemap::LineRangeUtils;
use comment::{rewrite_comment, CodeCharKind, CommentCodeSlices};
use config::WriteMode;
use shape::{Indent, Shape};
use utils::{count_newlines, last_line_width, mk_sp};
use visitor::FmtVisitor;

struct SnippetStatus {
    /// An offset to the current line from the beginnig of the original snippet.
    line_start: usize,
    /// A length of trailing whitespaces on the current line.
    last_wspace: Option<usize>,
    /// The current line number.
    cur_line: usize,
}

impl SnippetStatus {
    fn new(cur_line: usize) -> Self {
        SnippetStatus {
            line_start: 0,
            last_wspace: None,
            cur_line,
        }
    }
}

impl<'a> FmtVisitor<'a> {
    fn output_at_start(&self) -> bool {
        self.buffer.is_empty()
    }

    // TODO these format_missing methods are ugly. Refactor and add unit tests
    // for the central whitespace stripping loop.
    pub fn format_missing(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| this.push_str(last_snippet))
    }

    pub fn format_missing_with_indent(&mut self, end: BytePos) {
        let config = self.config;
        self.format_missing_inner(end, |this, last_snippet, snippet| {
            this.push_str(last_snippet.trim_right());
            if last_snippet == snippet && !this.output_at_start() {
                // No new lines in the snippet.
                this.push_str("\n");
            }
            let indent = this.block_indent.to_string(config);
            this.push_str(&indent);
        })
    }

    pub fn format_missing_no_indent(&mut self, end: BytePos) {
        self.format_missing_inner(end, |this, last_snippet, _| {
            this.push_str(last_snippet.trim_right());
        })
    }

    fn format_missing_inner<F: Fn(&mut FmtVisitor, &str, &str)>(
        &mut self,
        end: BytePos,
        process_last_snippet: F,
    ) {
        let start = self.last_pos;

        if start == end {
            // Do nothing if this is the beginning of the file.
            if !self.output_at_start() {
                process_last_snippet(self, "", "");
            }
            return;
        }

        assert!(
            start < end,
            "Request to format inverted span: {:?} to {:?}",
            self.codemap.lookup_char_pos(start),
            self.codemap.lookup_char_pos(end)
        );

        self.last_pos = end;
        let span = mk_sp(start, end);
        let snippet = self.snippet(span);
        if snippet.trim().is_empty() && !out_of_file_lines_range!(self, span) {
            // Keep vertical spaces within range.
            self.push_vertical_spaces(count_newlines(snippet));
            process_last_snippet(self, "", snippet);
        } else {
            self.write_snippet(span, &process_last_snippet);
        }
    }

    fn push_vertical_spaces(&mut self, mut newline_count: usize) {
        // The buffer already has a trailing newline.
        let offset = if self.buffer.ends_with('\n') { 0 } else { 1 };
        let newline_upper_bound = self.config.blank_lines_upper_bound() + offset;
        let newline_lower_bound = self.config.blank_lines_lower_bound() + offset;
        if newline_count > newline_upper_bound {
            newline_count = newline_upper_bound;
        } else if newline_count < newline_lower_bound {
            newline_count = newline_lower_bound;
        }
        let blank_lines: String = repeat('\n').take(newline_count).collect();
        self.push_str(&blank_lines);
    }

    fn write_snippet<F>(&mut self, span: Span, process_last_snippet: F)
    where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Get a snippet from the file start to the span's hi without allocating.
        // We need it to determine what precedes the current comment. If the comment
        // follows code on the same line, we won't touch it.
        let big_span_lo = self.codemap.lookup_char_pos(span.lo()).file.start_pos;
        let local_begin = self.codemap.lookup_byte_offset(big_span_lo);
        let local_end = self.codemap.lookup_byte_offset(span.hi());
        let start_index = local_begin.pos.to_usize();
        let end_index = local_end.pos.to_usize();
        let big_snippet = &local_begin.fm.src.as_ref().unwrap()[start_index..end_index];

        let big_diff = (span.lo() - big_span_lo).to_usize();
        let snippet = self.snippet(span);

        debug!("write_snippet `{}`", snippet);

        self.write_snippet_inner(big_snippet, big_diff, snippet, span, process_last_snippet);
    }

    fn write_snippet_inner<F>(
        &mut self,
        big_snippet: &str,
        big_diff: usize,
        old_snippet: &str,
        span: Span,
        process_last_snippet: F,
    ) where
        F: Fn(&mut FmtVisitor, &str, &str),
    {
        // Trim whitespace from the right hand side of each line.
        // Annoyingly, the library functions for splitting by lines etc. are not
        // quite right, so we must do it ourselves.
        let char_pos = self.codemap.lookup_char_pos(span.lo());
        let file_name = &char_pos.file.name;
        let mut status = SnippetStatus::new(char_pos.line);

        let snippet = &*match self.config.write_mode() {
            WriteMode::Coverage => replace_chars(old_snippet),
            _ => Cow::from(old_snippet),
        };

        for (kind, offset, subslice) in CommentCodeSlices::new(snippet) {
            debug!("{:?}: {:?}", kind, subslice);

            let newline_count = count_newlines(subslice);
            let within_file_lines_range = self.config.file_lines().intersects_range(
                file_name,
                status.cur_line,
                status.cur_line + newline_count,
            );

            if CodeCharKind::Comment == kind && within_file_lines_range {
                // 1: comment.
                self.process_comment(
                    &mut status,
                    snippet,
                    &big_snippet[..(offset + big_diff)],
                    offset,
                    subslice,
                );
            } else if subslice.trim().is_empty() && newline_count > 0 && within_file_lines_range {
                // 2: blank lines.
                self.push_vertical_spaces(newline_count);
                status.cur_line += newline_count;
                status.line_start = offset + newline_count;
            } else {
                // 3: code which we failed to format or which is not within file-lines range.
                self.process_missing_code(&mut status, snippet, subslice, offset, file_name);
            }
        }

        process_last_snippet(self, &snippet[status.line_start..], snippet);
    }

    fn process_comment(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        big_snippet: &str,
        offset: usize,
        subslice: &str,
    ) {
        let last_char = big_snippet
            .chars()
            .rev()
            .skip_while(|rev_c| [' ', '\t'].contains(rev_c))
            .next();

        let fix_indent = last_char.map_or(true, |rev_c| ['{', '\n'].contains(&rev_c));

        if fix_indent {
            if let Some('{') = last_char {
                self.push_str("\n");
            }
            let indent_str = self.block_indent.to_string(self.config);
            self.push_str(&indent_str);
        } else {
            self.push_str(" ");
        }

        let comment_width = ::std::cmp::min(
            self.config.comment_width(),
            self.config.max_width() - self.block_indent.width(),
        );
        let comment_indent = Indent::from_width(self.config, last_line_width(&self.buffer));
        let comment_shape = Shape::legacy(comment_width, comment_indent);
        let comment_str = rewrite_comment(subslice, false, comment_shape, self.config)
            .unwrap_or_else(|| String::from(subslice));
        self.push_str(&comment_str);

        status.last_wspace = None;
        status.line_start = offset + subslice.len();

        if let Some('/') = subslice.chars().nth(1) {
            // check that there are no contained block comments
            if !subslice
                .split('\n')
                .map(|s| s.trim_left())
                .any(|s| s.len() >= 2 && &s[0..2] == "/*")
            {
                // Add a newline after line comments
                self.push_str("\n");
            }
        } else if status.line_start <= snippet.len() {
            // For other comments add a newline if there isn't one at the end already
            match snippet[status.line_start..].chars().next() {
                Some('\n') | Some('\r') => (),
                _ => self.push_str("\n"),
            }
        }

        status.cur_line += count_newlines(subslice);
    }

    fn process_missing_code(
        &mut self,
        status: &mut SnippetStatus,
        snippet: &str,
        subslice: &str,
        offset: usize,
        file_name: &str,
    ) {
        for (mut i, c) in subslice.char_indices() {
            i += offset;

            if c == '\n' {
                let skip_this_line = !self.config
                    .file_lines()
                    .contains_line(file_name, status.cur_line);
                if skip_this_line {
                    status.last_wspace = None;
                }

                if let Some(lw) = status.last_wspace {
                    self.push_str(&snippet[status.line_start..lw]);
                    self.push_str("\n");
                    status.last_wspace = None;
                } else {
                    self.push_str(&snippet[status.line_start..i + 1]);
                }

                status.cur_line += 1;
                status.line_start = i + 1;
            } else if c.is_whitespace() && status.last_wspace.is_none() {
                status.last_wspace = Some(i);
            } else if c == ';' && status.last_wspace.is_some() {
                status.line_start = i;
                status.last_wspace = None;
            } else {
                status.last_wspace = None;
            }
        }

        let remaining = snippet[status.line_start..subslice.len() + offset].trim();
        if !remaining.is_empty() {
            self.push_str(remaining);
            status.line_start = subslice.len() + offset;
        }
    }
}

fn replace_chars(string: &str) -> Cow<str> {
    Cow::from(
        string
            .chars()
            .map(|ch| if ch.is_whitespace() { ch } else { 'X' })
            .collect::<String>(),
    )
}
<|endoftext|>


<commit_before>use super::*;
use ::internal::prelude::*;

#[cfg(feature="cache")]
use super::{permissions, utils as model_utils};
#[cfg(feature="model")]
use ::http;
#[cfg(feature="model")]
use ::builder::CreateInvite;

/// Information about an invite code.
///
/// Information can not be accessed for guilds the current user is banned from.
#[derive(Clone, Debug, Deserialize)]
pub struct Invite {
    /// The approximate number of [`Member`]s in the related [`Guild`].
    ///
    /// [`Guild`]: struct.Guild.html
    /// [`Member`]: struct.Member.html
    pub approximate_member_count: Option<u64>,
    /// The approximate number of [`Member`]s with an active session in the
    /// related [`Guild`].
    ///
    /// An active session is defined as an open, heartbeating WebSocket connection.
    /// These include [invisible][`OnlineStatus::Invisible`] members.
    ///
    /// [`OnlineStatus::Invisible`]: enum.OnlineStatus.html#variant.Invisible
    pub approximate_presence_count: Option<u64>,
    /// The unique code for the invite.
    pub code: String,
    /// A representation of the minimal amount of information needed about the
    /// [`GuildChannel`] being invited to.
    ///
    /// [`GuildChannel`]: struct.GuildChannel.html
    pub channel: InviteChannel,
    /// a representation of the minimal amount of information needed about the
    /// [`Guild`] being invited to.
    pub guild: InviteGuild,
}

#[cfg(feature="model")]
impl Invite {
    /// Creates an invite for a [`GuildChannel`], providing a builder so that
    /// fields may optionally be set.
    ///
    /// See the documentation for the [`CreateInvite`] builder for information
    /// on how to use this and the default values that it provides.
    ///
    /// Requires the [Create Invite] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`CreateInvite`]: ../builder/struct.CreateInvite.html
    /// [`GuildChannel`]: struct.GuildChannel.html
    /// [Create Invite]: permissions/constant.CREATE_INVITE.html
    /// [permission]: permissions/index.html
    pub fn create<C, F>(channel_id: C, f: F) -> Result<RichInvite>
        where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
        let channel_id = channel_id.into();

        #[cfg(feature="cache")]
        {
            let req = permissions::CREATE_INVITE;

            if !model_utils::user_has_perms(channel_id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::create_invite(channel_id.0, &f(CreateInvite::default()).0)
    }

    /// Deletes the invite.
    ///
    /// **Note**: Requires the [Manage Guild] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
    /// [permission]: permissions/index.html
    pub fn delete(&self) -> Result<Invite> {
        #[cfg(feature="cache")]
        {
            let req = permissions::MANAGE_GUILD;

            if !model_utils::user_has_perms(self.channel.id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::delete_invite(&self.code)
    }

    /// Gets the information about an invite.
    pub fn get(code: &str, stats: bool) -> Result<Invite> {
        let mut invite = code;

        #[cfg(feature="utils")]
        {
            invite = ::utils::parse_invite(invite);
        }

        http::get_invite(invite, stats)
    }

    /// Returns a URL to use for the invite.
    ///
    /// # Examples
    ///
    /// Retrieve the URL for an invite with the code `WxZumR`:
    ///
    /// ```rust
    /// # use serenity::model::*;
    /// #
    /// # let invite = Invite {
    /// #     code: "WxZumR".to_owned(),
    /// #     channel: InviteChannel {
    /// #         id: ChannelId(1),
    /// #         name: "foo".to_owned(),
    /// #         kind: ChannelType::Text,
    /// #     },
    /// #     guild: InviteGuild {
    /// #         id: GuildId(2),
    /// #         icon: None,
    /// #         name: "bar".to_owned(),
    /// #         splash_hash: None,
    /// #     },
    /// # };
    /// #
    /// assert_eq!(invite.url(), "https://discord.gg/WxZumR");
    /// ```
    pub fn url(&self) -> String {
        format!("https://discord.gg/{}", self.code)
    }
}

/// A inimal information about the channel an invite points to.
#[derive(Clone, Debug, Deserialize)]
pub struct InviteChannel {
    pub id: ChannelId,
    pub name: String,
    #[serde(rename="type")]
    pub kind: ChannelType,
}

/// A minimal amount of information about the guild an invite points to.
#[derive(Clone, Debug, Deserialize)]
pub struct InviteGuild {
    pub id: GuildId,
    pub icon: Option<String>,
    pub name: String,
    pub splash_hash: Option<String>,
    pub text_channel_count: Option<u64>,
    pub voice_channel_count: Option<u64>,
}

#[cfg(feature="model")]
impl InviteGuild {
    /// Returns the Id of the shard associated with the guild.
    ///
    /// When the cache is enabled this will automatically retrieve the total
    /// number of shards.
    ///
    /// **Note**: When the cache is enabled, this function unlocks the cache to
    /// retrieve the total number of shards in use. If you already have the
    /// total, consider using [`utils::shard_id`].
    ///
    /// [`utils::shard_id`]: ../utils/fn.shard_id.html
    #[cfg(all(feature="cache", feature="utils"))]
    #[inline]
    pub fn shard_id(&self) -> u64 {
        self.id.shard_id()
    }

    /// Returns the Id of the shard associated with the guild.
    ///
    /// When the cache is enabled this will automatically retrieve the total
    /// number of shards.
    ///
    /// When the cache is not enabled, the total number of shards being used
    /// will need to be passed.
    ///
    /// # Examples
    ///
    /// Retrieve the Id of the shard for a guild with Id `81384788765712384`,
    /// using 17 shards:
    ///
    /// ```rust,ignore
    /// use serenity::utils;
    ///
    /// // assumes a `guild` has already been bound
    ///
    /// assert_eq!(guild.shard_id(17), 7);
    /// ```
    #[cfg(all(feature="utils", not(feature="cache")))]
    #[inline]
    pub fn shard_id(&self, shard_count: u64) -> u64 {
        self.id.shard_id(shard_count)
    }
}

/// Detailed information about an invite.
/// This information can only be retrieved by anyone with the [Manage Guild]
/// permission. Otherwise, a minimal amount of information can be retrieved via
/// the [`Invite`] struct.
///
/// [`Invite`]: struct.Invite.html
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[derive(Clone, Debug, Deserialize)]
pub struct RichInvite {
    /// A representation of the minimal amount of information needed about the
    /// channel being invited to.
    pub channel: InviteChannel,
    /// The unique code for the invite.
    pub code: String,
    /// When the invite was created.
    pub created_at: String,
    /// A representation of the minimal amount of information needed about the
    /// guild being invited to.
    pub guild: InviteGuild,
    /// The user that created the invite.
    pub inviter: User,
    /// The maximum age of the invite in seconds, from when it was created.
    pub max_age: u64,
    /// The maximum number of times that an invite may be used before it expires.

    /// Note that this does not supercede the [`max_age`] value, if the value of
    /// [`temporary`] is `true`. If the value of `temporary` is `false`, then the
    /// invite _will_ self-expire after the given number of max uses.

    /// If the value is `0`, then the invite is permanent.
    ///
    /// [`max_age`]: #structfield.max_age
    /// [`temporary`]: #structfield.temporary
    pub max_uses: u64,
    /// Indicator of whether the invite self-expires after a certain amount of
    /// time or uses.
    pub temporary: bool,
    /// The amount of times that an invite has been used.
    pub uses: u64,
}

#[cfg(feature="model")]
impl RichInvite {
    /// Deletes the invite.
    ///
    /// Refer to [`http::delete_invite`] for more information.
    ///
    /// **Note**: Requires the [Manage Guild] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` feature is enabled, then this returns a
    /// [`ModelError::InvalidPermissions`] if the current user does not have
    /// the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`Invite::delete`]: struct.Invite.html#method.delete
    /// [`http::delete_invite`]: ../http/fn.delete_invite.html
    /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
    /// [permission]: permissions/index.html
    pub fn delete(&self) -> Result<Invite> {
        #[cfg(feature="cache")]
        {
            let req = permissions::MANAGE_GUILD;

            if !model_utils::user_has_perms(self.channel.id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::delete_invite(&self.code)
    }

    /// Returns a URL to use for the invite.
    ///
    /// # Examples
    ///
    /// Retrieve the URL for an invite with the code `WxZumR`:
    ///
    /// ```rust
    /// # use serenity::model::*;
    /// #
    /// # let invite = RichInvite {
    /// #     code: "WxZumR".to_owned(),
    /// #     channel: InviteChannel {
    /// #         id: ChannelId(1),
    /// #         name: "foo".to_owned(),
    /// #         kind: ChannelType::Text,
    /// #     },
    /// #     created_at: "bar".to_owned(),
    /// #     guild: InviteGuild {
    /// #         id: GuildId(2),
    /// #         icon: None,
    /// #         name: "baz".to_owned(),
    /// #         splash_hash: None,
    /// #     },
    /// #     inviter: User {
    /// #         avatar: None,
    /// #         bot: false,
    /// #         discriminator: 3,
    /// #         id: UserId(4),
    /// #         name: "qux".to_owned(),
    /// #     },
    /// #     max_age: 5,
    /// #     max_uses: 6,
    /// #     temporary: true,
    /// #     uses: 7,
    /// # };
    /// #
    /// assert_eq!(invite.url(), "https://discord.gg/WxZumR");
    /// ```
    pub fn url(&self) -> String {
        format!("https://discord.gg/{}", self.code)
    }
}
<commit_msg>Fix {Invite,RichInvite}::url tests<commit_after>use super::*;
use ::internal::prelude::*;

#[cfg(feature="cache")]
use super::{permissions, utils as model_utils};
#[cfg(feature="model")]
use ::http;
#[cfg(feature="model")]
use ::builder::CreateInvite;

/// Information about an invite code.
///
/// Information can not be accessed for guilds the current user is banned from.
#[derive(Clone, Debug, Deserialize)]
pub struct Invite {
    /// The approximate number of [`Member`]s in the related [`Guild`].
    ///
    /// [`Guild`]: struct.Guild.html
    /// [`Member`]: struct.Member.html
    pub approximate_member_count: Option<u64>,
    /// The approximate number of [`Member`]s with an active session in the
    /// related [`Guild`].
    ///
    /// An active session is defined as an open, heartbeating WebSocket connection.
    /// These include [invisible][`OnlineStatus::Invisible`] members.
    ///
    /// [`OnlineStatus::Invisible`]: enum.OnlineStatus.html#variant.Invisible
    pub approximate_presence_count: Option<u64>,
    /// The unique code for the invite.
    pub code: String,
    /// A representation of the minimal amount of information needed about the
    /// [`GuildChannel`] being invited to.
    ///
    /// [`GuildChannel`]: struct.GuildChannel.html
    pub channel: InviteChannel,
    /// a representation of the minimal amount of information needed about the
    /// [`Guild`] being invited to.
    pub guild: InviteGuild,
}

#[cfg(feature="model")]
impl Invite {
    /// Creates an invite for a [`GuildChannel`], providing a builder so that
    /// fields may optionally be set.
    ///
    /// See the documentation for the [`CreateInvite`] builder for information
    /// on how to use this and the default values that it provides.
    ///
    /// Requires the [Create Invite] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`CreateInvite`]: ../builder/struct.CreateInvite.html
    /// [`GuildChannel`]: struct.GuildChannel.html
    /// [Create Invite]: permissions/constant.CREATE_INVITE.html
    /// [permission]: permissions/index.html
    pub fn create<C, F>(channel_id: C, f: F) -> Result<RichInvite>
        where C: Into<ChannelId>, F: FnOnce(CreateInvite) -> CreateInvite {
        let channel_id = channel_id.into();

        #[cfg(feature="cache")]
        {
            let req = permissions::CREATE_INVITE;

            if !model_utils::user_has_perms(channel_id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::create_invite(channel_id.0, &f(CreateInvite::default()).0)
    }

    /// Deletes the invite.
    ///
    /// **Note**: Requires the [Manage Guild] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
    /// [permission]: permissions/index.html
    pub fn delete(&self) -> Result<Invite> {
        #[cfg(feature="cache")]
        {
            let req = permissions::MANAGE_GUILD;

            if !model_utils::user_has_perms(self.channel.id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::delete_invite(&self.code)
    }

    /// Gets the information about an invite.
    pub fn get(code: &str, stats: bool) -> Result<Invite> {
        let mut invite = code;

        #[cfg(feature="utils")]
        {
            invite = ::utils::parse_invite(invite);
        }

        http::get_invite(invite, stats)
    }

    /// Returns a URL to use for the invite.
    ///
    /// # Examples
    ///
    /// Retrieve the URL for an invite with the code `WxZumR`:
    ///
    /// ```rust
    /// # use serenity::model::*;
    /// #
    /// # let invite = Invite {
    /// #     approximate_member_count: Some(1812),
    /// #     approximate_presence_count: Some(717),
    /// #     code: "WxZumR".to_owned(),
    /// #     channel: InviteChannel {
    /// #         id: ChannelId(1),
    /// #         name: "foo".to_owned(),
    /// #         kind: ChannelType::Text,
    /// #     },
    /// #     guild: InviteGuild {
    /// #         id: GuildId(2),
    /// #         icon: None,
    /// #         name: "bar".to_owned(),
    /// #         splash_hash: None,
    /// #         text_channel_count: Some(7),
    /// #         voice_channel_count: Some(3),
    /// #     },
    /// # };
    /// #
    /// assert_eq!(invite.url(), "https://discord.gg/WxZumR");
    /// ```
    pub fn url(&self) -> String {
        format!("https://discord.gg/{}", self.code)
    }
}

/// A inimal information about the channel an invite points to.
#[derive(Clone, Debug, Deserialize)]
pub struct InviteChannel {
    pub id: ChannelId,
    pub name: String,
    #[serde(rename="type")]
    pub kind: ChannelType,
}

/// A minimal amount of information about the guild an invite points to.
#[derive(Clone, Debug, Deserialize)]
pub struct InviteGuild {
    pub id: GuildId,
    pub icon: Option<String>,
    pub name: String,
    pub splash_hash: Option<String>,
    pub text_channel_count: Option<u64>,
    pub voice_channel_count: Option<u64>,
}

#[cfg(feature="model")]
impl InviteGuild {
    /// Returns the Id of the shard associated with the guild.
    ///
    /// When the cache is enabled this will automatically retrieve the total
    /// number of shards.
    ///
    /// **Note**: When the cache is enabled, this function unlocks the cache to
    /// retrieve the total number of shards in use. If you already have the
    /// total, consider using [`utils::shard_id`].
    ///
    /// [`utils::shard_id`]: ../utils/fn.shard_id.html
    #[cfg(all(feature="cache", feature="utils"))]
    #[inline]
    pub fn shard_id(&self) -> u64 {
        self.id.shard_id()
    }

    /// Returns the Id of the shard associated with the guild.
    ///
    /// When the cache is enabled this will automatically retrieve the total
    /// number of shards.
    ///
    /// When the cache is not enabled, the total number of shards being used
    /// will need to be passed.
    ///
    /// # Examples
    ///
    /// Retrieve the Id of the shard for a guild with Id `81384788765712384`,
    /// using 17 shards:
    ///
    /// ```rust,ignore
    /// use serenity::utils;
    ///
    /// // assumes a `guild` has already been bound
    ///
    /// assert_eq!(guild.shard_id(17), 7);
    /// ```
    #[cfg(all(feature="utils", not(feature="cache")))]
    #[inline]
    pub fn shard_id(&self, shard_count: u64) -> u64 {
        self.id.shard_id(shard_count)
    }
}

/// Detailed information about an invite.
/// This information can only be retrieved by anyone with the [Manage Guild]
/// permission. Otherwise, a minimal amount of information can be retrieved via
/// the [`Invite`] struct.
///
/// [`Invite`]: struct.Invite.html
/// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
#[derive(Clone, Debug, Deserialize)]
pub struct RichInvite {
    /// A representation of the minimal amount of information needed about the
    /// channel being invited to.
    pub channel: InviteChannel,
    /// The unique code for the invite.
    pub code: String,
    /// When the invite was created.
    pub created_at: String,
    /// A representation of the minimal amount of information needed about the
    /// guild being invited to.
    pub guild: InviteGuild,
    /// The user that created the invite.
    pub inviter: User,
    /// The maximum age of the invite in seconds, from when it was created.
    pub max_age: u64,
    /// The maximum number of times that an invite may be used before it expires.

    /// Note that this does not supercede the [`max_age`] value, if the value of
    /// [`temporary`] is `true`. If the value of `temporary` is `false`, then the
    /// invite _will_ self-expire after the given number of max uses.

    /// If the value is `0`, then the invite is permanent.
    ///
    /// [`max_age`]: #structfield.max_age
    /// [`temporary`]: #structfield.temporary
    pub max_uses: u64,
    /// Indicator of whether the invite self-expires after a certain amount of
    /// time or uses.
    pub temporary: bool,
    /// The amount of times that an invite has been used.
    pub uses: u64,
}

#[cfg(feature="model")]
impl RichInvite {
    /// Deletes the invite.
    ///
    /// Refer to [`http::delete_invite`] for more information.
    ///
    /// **Note**: Requires the [Manage Guild] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` feature is enabled, then this returns a
    /// [`ModelError::InvalidPermissions`] if the current user does not have
    /// the required [permission].
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`Invite::delete`]: struct.Invite.html#method.delete
    /// [`http::delete_invite`]: ../http/fn.delete_invite.html
    /// [Manage Guild]: permissions/constant.MANAGE_GUILD.html
    /// [permission]: permissions/index.html
    pub fn delete(&self) -> Result<Invite> {
        #[cfg(feature="cache")]
        {
            let req = permissions::MANAGE_GUILD;

            if !model_utils::user_has_perms(self.channel.id, req)? {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        http::delete_invite(&self.code)
    }

    /// Returns a URL to use for the invite.
    ///
    /// # Examples
    ///
    /// Retrieve the URL for an invite with the code `WxZumR`:
    ///
    /// ```rust
    /// # use serenity::model::*;
    /// #
    /// # let invite = RichInvite {
    /// #     code: "WxZumR".to_owned(),
    /// #     channel: InviteChannel {
    /// #         id: ChannelId(1),
    /// #         name: "foo".to_owned(),
    /// #         kind: ChannelType::Text,
    /// #     },
    /// #     created_at: "bar".to_owned(),
    /// #     guild: InviteGuild {
    /// #         id: GuildId(2),
    /// #         icon: None,
    /// #         name: "baz".to_owned(),
    /// #         splash_hash: None,
    /// #         text_channel_count: None,
    /// #         voice_channel_count: None,
    /// #     },
    /// #     inviter: User {
    /// #         avatar: None,
    /// #         bot: false,
    /// #         discriminator: 3,
    /// #         id: UserId(4),
    /// #         name: "qux".to_owned(),
    /// #     },
    /// #     max_age: 5,
    /// #     max_uses: 6,
    /// #     temporary: true,
    /// #     uses: 7,
    /// # };
    /// #
    /// assert_eq!(invite.url(), "https://discord.gg/WxZumR");
    /// ```
    pub fn url(&self) -> String {
        format!("https://discord.gg/{}", self.code)
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code, non_snake_case)]

/// This file contains various traits/functions for interacting with Cocoa/objc classes which
/// are not available in the cocoa/objc crates. They should probably be migrated to them soon.

use cocoa::base::{class, id, BOOL};
use cocoa::foundation::NSUInteger;
use std::any::Any;

pub trait NSArray {
    unsafe fn arrayWithArray(array: id) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithArray:array]
    }
    unsafe fn arrayWithObject(object: id) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithObject:object]
    }
    unsafe fn arrayWithObjects_count(objects: *mut id, count: NSUInteger) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithObjects:objects count:count]
    }

    unsafe fn containsObject(self, anObject: id) -> BOOL;
    unsafe fn count(self) -> NSUInteger;
    unsafe fn firstObject(self) -> id;
    unsafe fn lastObject(self) -> id;
    unsafe fn objectAtIndex(self, index: NSUInteger) -> id;
    unsafe fn indexOfObject(self, anObject: id) -> NSUInteger;
    unsafe fn indexOfObjectIdenticalTo(self, anObject: id) -> NSUInteger;
}

impl NSArray for id {
    unsafe fn containsObject(self, anObject: id) -> BOOL {
        msg_send![self, containsObject:anObject]
    }

    unsafe fn count(self) -> NSUInteger {
        msg_send![self, count]
    }

    unsafe fn firstObject(self) -> id {
        msg_send![self, firstObject]
    }

    unsafe fn lastObject(self) -> id {
        msg_send![self, lastObject]
    }

    unsafe fn objectAtIndex(self, index: NSUInteger) -> id {
        msg_send![self, objectAtIndex:index]
    }

    unsafe fn indexOfObject(self, anObject: id) -> NSUInteger {
        msg_send![self, indexOfObject:anObject]
    }

    unsafe fn indexOfObjectIdenticalTo(self, anObject: id) -> NSUInteger {
        msg_send![self, indexOfObjectIdenticalTo:anObject]
    }
}

pub trait NSDictionary {

}
<commit_msg>Add NSDictionary to objc_bringup<commit_after>#![allow(dead_code, non_snake_case)]

/// This file contains various traits/functions for interacting with Cocoa/objc classes which
/// are not available in the cocoa/objc crates. They should probably be migrated to them soon.

use cocoa::base::{class, id, BOOL};
use cocoa::foundation::NSUInteger;
use std::any::Any;

pub trait NSArray {
    unsafe fn arrayWithArray(array: id) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithArray:array]
    }
    unsafe fn arrayWithObject(object: id) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithObject:object]
    }
    unsafe fn arrayWithObjects_count(objects: *mut id, count: NSUInteger) -> Self where Self: Sized + Any {
        msg_send![class("NSArray"), arrayWithObjects:objects count:count]
    }

    unsafe fn containsObject(self, anObject: id) -> BOOL;
    unsafe fn count(self) -> NSUInteger;
    unsafe fn firstObject(self) -> id;
    unsafe fn lastObject(self) -> id;
    unsafe fn objectAtIndex(self, index: NSUInteger) -> id;
    unsafe fn indexOfObject(self, anObject: id) -> NSUInteger;
    unsafe fn indexOfObjectIdenticalTo(self, anObject: id) -> NSUInteger;
}

impl NSArray for id {
    unsafe fn containsObject(self, anObject: id) -> BOOL {
        msg_send![self, containsObject:anObject]
    }

    unsafe fn count(self) -> NSUInteger {
        msg_send![self, count]
    }

    unsafe fn firstObject(self) -> id {
        msg_send![self, firstObject]
    }

    unsafe fn lastObject(self) -> id {
        msg_send![self, lastObject]
    }

    unsafe fn objectAtIndex(self, index: NSUInteger) -> id {
        msg_send![self, objectAtIndex:index]
    }

    unsafe fn indexOfObject(self, anObject: id) -> NSUInteger {
        msg_send![self, indexOfObject:anObject]
    }

    unsafe fn indexOfObjectIdenticalTo(self, anObject: id) -> NSUInteger {
        msg_send![self, indexOfObjectIdenticalTo:anObject]
    }
}

pub trait NSDictionary {
    unsafe fn dictionaryWithObjects_forKeys_count(objects: *mut id, keys: *mut id, count: NSUInteger) -> Self
        where Self: Sized + Any {
        msg_send![class("NSDictionary"), dictionaryWithObjects:objects forKeys:keys count:count]
    }

    unsafe fn count(self) -> NSUInteger;
    unsafe fn objectForKey(self, key:id) -> id;
}

impl NSDictionary for id {
    unsafe fn count(self) -> NSUInteger {
        msg_send![self, count]
    }

    unsafe fn objectForKey(self, key:id) -> id {
        msg_send![self, objectForKey:key]
    }
}
<|endoftext|>