<commit_before>use std::io::File;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(save_data.as_slice()).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(save_data.as_slice()).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

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

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<commit_msg>Screenshotting<commit_after>use std::io::File;
use std::io::fs::PathExtensions;
use std::collections::HashMap;
use util::{color, V2};
use backend::{Canvas, CanvasUtil, Event, Key, Fonter};
use world;
use world::action;
use world::action::Input::{Step, Melee};
use world::action::ControlState::*;
use world::{Msg, FovStatus};
use world::Dir6;
use world::Dir6::*;
use world::{Entity};
use world::item::{Slot};
use worldview;
use sprite::{WorldSprites, GibSprite};
use tilecache;
use tilecache::icon;
use msg_queue::MsgQueue;

pub struct GameState {
    /// Transient effect sprites drawn in game world view.
    world_spr: WorldSprites,
    /// Counters for entities with flashing damage animation.
    damage_timers: HashMap<Entity, u32>,

    /// Flag for autoexploration.
    // TODO: Probably going to need a general "ongoing activity" system at
    // some point.
    exploring: bool,

    msg: MsgQueue,
    ui_state: UiState,

    // Hacky thing to wait for next time Canvas reference is available.
    screenshot_requested: bool,
}

enum UiState {
    Gameplay,
    Inventory
}

impl GameState {
    pub fn new(seed: Option<u32>) -> GameState {
        world::init_world(seed);
        GameState {
            world_spr: WorldSprites::new(),
            damage_timers: HashMap::new(),
            exploring: false,
            msg: MsgQueue::new(),
            ui_state: UiState::Gameplay,
            screenshot_requested: false,
        }
    }

    fn draw_player_ui(&mut self, ctx: &mut Canvas, player: Entity) {
        let hp = player.hp();
        let max_hp = player.max_hp();

        // Draw heart containers.
        for i in 0..((max_hp + 1) / 2) {
            let pos = V2(i as f32 * 8.0, 8.0);
            let idx = if hp >= (i + 1) * 2 { icon::HEART }
                else if hp == i * 2 + 1 { icon::HALF_HEART }
                else { icon::NO_HEART };
            ctx.draw_image(tilecache::get(idx), pos, 0.0, &color::FIREBRICK, &color::BLACK);
        }
    }

    fn base_paint(&mut self, ctx: &mut Canvas) {
        let camera = world::camera();
        worldview::draw_world(&camera, ctx, &self.damage_timers);

        self.world_spr.draw(|x| (camera + x).fov_status() == Some(FovStatus::Seen), &camera, ctx);
        self.world_spr.update();

        let location_name = camera.name();
        write!(&mut ctx.text_writer(V2(640 - location_name.len() as i32 * 8, 8), 0.1, color::LIGHTGRAY)
                       .set_border(color::BLACK),
                       "{}", location_name).unwrap();

        self.msg.draw(ctx);
        if let Some(player) = action::player() {
            self.draw_player_ui(ctx, player);
        }

        let fps = 1.0 / ctx.render_duration;
        let _ = write!(&mut ctx.text_writer(V2(0, 16), 0.1, color::LIGHTGREEN)
                       .set_border(color::BLACK),
                       "FPS {:.0}", fps);
    }

    fn base_update(&mut self, ctx: &mut Canvas) {
        // Process events
        loop {
            match world::pop_msg() {
                Some(Msg::Gib(loc)) => {
                    self.world_spr.add(Box::new(GibSprite::new(loc)));
                }
                Some(Msg::Damage(entity)) => {
                    self.damage_timers.insert(entity, 2);
                }
                Some(Msg::Text(txt)) => {
                    self.msg.msg(txt)
                }
                Some(Msg::Caption(txt)) => {
                    self.msg.caption(txt)
                }
                Some(x) => {
                    println!("Unhandled Msg type {:?}", x);
                }
                None => break
            }
        }

        self.base_paint(ctx);

        if action::control_state() == ReadyToUpdate {
            action::update();
        }

        if self.exploring {
            if action::control_state() == AwaitingInput {
                self.exploring = self.autoexplore();
            }
        }

        self.damage_timers = self.damage_timers.clone().into_iter()
            .filter(|&(_, t)| t > 0)
            .map(|(e, t)| (e, t - 1))
            .collect();

        self.msg.update();
    }

    fn inventory_update(&mut self, ctx: &mut Canvas) {
        let player = action::player().unwrap();
        {
            let mut cursor = ctx.text_writer(V2(0, 8), 0.1, color::GAINSBORO);
            for slot_data in SLOT_DATA.iter() {
                let name = match player.equipped(slot_data.slot) {
                    Some(item) => item.name(),
                    None => "".to_string()
                };
                write!(&mut cursor, "{}] {}: {}\n",
                    slot_data.key, slot_data.name, name).unwrap();
            }
        }

        {
            let mut cursor = ctx.text_writer(V2(0, 360), 0.1, color::GAINSBORO);
            write!(&mut cursor, "Press letter to equip/unequip item. Press shift+letter to drop item.").unwrap();
        }
    }

    pub fn inventory_process(&mut self, event: Event) -> bool {
        let player = action::player().unwrap();
        match event {
            Event::Render(ctx) => { self.update(ctx); }
            Event::KeyPressed(Key::Escape) | Event::KeyPressed(Key::Tab) => {
                self.ui_state = UiState::Gameplay
            }
            Event::KeyPressed(Key::F12) => { self.screenshot_requested = true; }
            Event::KeyPressed(_) => {}

            Event::Char(ch) => {
                for slot_data in SLOT_DATA.iter() {
                    if ch == slot_data.key {
                        if slot_data.slot.is_gear_slot() {
                            // Unequip gear
                            match player.free_bag_slot() {
                                None => {
                                    // No room in bag, can't unequip until
                                    // drop something.
                                    // TODO: Message about full bag.
                                }
                                Some(swap_slot) => {
                                    player.swap_equipped(slot_data.slot, swap_slot);
                                }
                            }
                        }
                        if slot_data.slot.is_bag_slot() {
                            // Bag items get equipped if they have are gear
                            // with a preferred slot.
                            if let Some(item) = player.equipped(slot_data.slot) {
                                let equip_slots = item.equip_slots();
                                for &swap_slot in equip_slots.iter() {
                                    if player.equipped(swap_slot).is_none() {
                                        player.swap_equipped(slot_data.slot, swap_slot);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ch == slot_data.key.to_uppercase() {
                        // Drop item in slot.
                        if let Some(item) = player.equipped(slot_data.slot) {
                            item.place(player.location().unwrap());
                        }
                        break;
                    }
                }
            }

            _ => ()
        }
        true
    }

    /// Repaint view, update game world if needed.
    pub fn update(&mut self, ctx: &mut Canvas) {
        if self.screenshot_requested {
            use std::io::File;
            use image;
            let shot = ctx.screenshot();
            let file = File::create(&Path::new("/tmp/shot.png")).unwrap();
            let _ = image::ImageRgb8(shot).save(file, image::PNG);

            self.screenshot_requested = false;
        }

        ctx.clear();

        match self.ui_state {
            UiState::Gameplay => self.base_update(ctx),
            UiState::Inventory => self.inventory_update(ctx),
        }
    }

    pub fn save_game(&self) {
        let save_data = world::save();
        let mut file = File::create(&Path::new("/tmp/magog_save.json"));
        file.write_str(save_data.as_slice()).unwrap();
    }

    pub fn load_game(&mut self) {
        let path = Path::new("/tmp/magog_save.json");
        if !path.exists() { return; }
        let save_data = File::open(&path).read_to_string().unwrap();
        // TODO: Handle failed load nicely.
        world::load(save_data.as_slice()).unwrap();
    }

    fn smart_move(&mut self, dir: Dir6) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        for &d in vec![dir, dir + 1, dir - 1].iter() {
            let target_loc = loc + d.to_v2();
            if target_loc.has_mobs() {
                action::input(Melee(d));
                return;
            } else if player.can_step(d) {
                action::input(Step(d));
                return;
            }
        }
    }

    fn autoexplore(&mut self) -> bool {
        let player = action::player().unwrap();
        if player.is_threatened() {
            return false;
        }
        if let Some(pathing) = action::autoexplore_map(32) {
            let loc = player.location().unwrap();
            let steps = pathing.sorted_neighbors(&loc);
            if steps.len() == 0 {
                return false;
            }

            action::input(Step(loc.dir6_towards(steps[0]).unwrap()));
            return true;
        }

        false
    }

    /// Context-specific interaction with the current cell.
    fn interact(&mut self) {
        let player = action::player().unwrap();
        let loc = player.location().unwrap();
        if let Some(item) = loc.top_item() {
            player.pick_up(item);
            return;
        }
    }

    /// Process a player control keypress.
    pub fn gameplay_process_key(&mut self, key: Key) -> bool {
        if action::control_state() != AwaitingInput {
            return false;
        }

        if self.exploring {
            self.exploring = false;
        }

        match key {
            Key::Q | Key::Pad7 => { self.smart_move(NorthWest); }
            Key::W | Key::Pad8 | Key::Up => { self.smart_move(North); }
            Key::E | Key::Pad9 => { self.smart_move(NorthEast); }
            Key::A | Key::Pad1 => { self.smart_move(SouthWest); }
            Key::S | Key::Pad2 | Key::Down => { self.smart_move(South); }
            Key::D | Key::Pad3 => { self.smart_move(SouthEast); }

            Key::Enter => { self.interact(); }
            Key::X => { self.exploring = true; }

            // Open inventory
            Key::Tab => { self.ui_state = UiState::Inventory; }

            Key::F5 => { self.save_game(); }
            Key::F9 => { self.load_game(); }
            Key::F12 => { self.screenshot_requested = true; }
            _ => { return false; }
        }
        return true;
    }

    pub fn gameplay_process(&mut self, event: Event) -> bool {
        match event {
            Event::Render(ctx) => {
                self.update(ctx);
            }
            // TODO: Better quit confirmation than just pressing esc.
            Event::KeyPressed(Key::Escape) => {
                return false;
            }
            Event::KeyPressed(k) => {
                self.gameplay_process_key(k);
            }

            Event::Char(ch) => {
                // TODO: Chars and keypresses in same lookup (use variants?)
                match ch {
                    // Debug
                    '>' => { action::next_level(); }
                    _ => ()
                }
            }

            _ => ()
        }
        true
    }

    pub fn process(&mut self, event: Event) -> bool {
        match self.ui_state {
            UiState::Gameplay => self.gameplay_process(event),
            UiState::Inventory => self.inventory_process(event),
        }
    }
}

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

static SLOT_DATA: [SlotData; 34] = [
    SlotData { key: '1', slot: Slot::Spell1,     name: "Ability" },
    SlotData { key: '2', slot: Slot::Spell2,     name: "Ability" },
    SlotData { key: '3', slot: Slot::Spell3,     name: "Ability" },
    SlotData { key: '4', slot: Slot::Spell4,     name: "Ability" },
    SlotData { key: '5', slot: Slot::Spell5,     name: "Ability" },
    SlotData { key: '6', slot: Slot::Spell6,     name: "Ability" },
    SlotData { key: '7', slot: Slot::Spell7,     name: "Ability" },
    SlotData { key: '8', slot: Slot::Spell8,     name: "Ability" },
    SlotData { key: 'a', slot: Slot::Melee,      name: "Weapon " },
    SlotData { key: 'b', slot: Slot::Ranged,     name: "Ranged " },
    SlotData { key: 'c', slot: Slot::Head,       name: "Head   " },
    SlotData { key: 'd', slot: Slot::Body,       name: "Body   " },
    SlotData { key: 'e', slot: Slot::Feet,       name: "Feet   " },
    SlotData { key: 'f', slot: Slot::TrinketF,   name: "Trinket" },
    SlotData { key: 'g', slot: Slot::TrinketG,   name: "Trinket" },
    SlotData { key: 'h', slot: Slot::TrinketH,   name: "Trinket" },
    SlotData { key: 'i', slot: Slot::TrinketI,   name: "Trinket" },
    SlotData { key: 'j', slot: Slot::InventoryJ, name: "       " },
    SlotData { key: 'k', slot: Slot::InventoryK, name: "       " },
    SlotData { key: 'l', slot: Slot::InventoryL, name: "       " },
    SlotData { key: 'm', slot: Slot::InventoryM, name: "       " },
    SlotData { key: 'n', slot: Slot::InventoryN, name: "       " },
    SlotData { key: 'o', slot: Slot::InventoryO, name: "       " },
    SlotData { key: 'p', slot: Slot::InventoryP, name: "       " },
    SlotData { key: 'q', slot: Slot::InventoryQ, name: "       " },
    SlotData { key: 'r', slot: Slot::InventoryR, name: "       " },
    SlotData { key: 's', slot: Slot::InventoryS, name: "       " },
    SlotData { key: 't', slot: Slot::InventoryT, name: "       " },
    SlotData { key: 'u', slot: Slot::InventoryU, name: "       " },
    SlotData { key: 'v', slot: Slot::InventoryV, name: "       " },
    SlotData { key: 'w', slot: Slot::InventoryW, name: "       " },
    SlotData { key: 'x', slot: Slot::InventoryX, name: "       " },
    SlotData { key: 'y', slot: Slot::InventoryY, name: "       " },
    SlotData { key: 'z', slot: Slot::InventoryZ, name: "       " },
];
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "52e3dffa50cfffdcfa145c0cc0ba48b49abc0c07";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/closing-args-token.rs",
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Update test suite to nightly-2021-05-04<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "716394d6581b60c75cfdd88b8e5b876f2db88b62";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // FIXME
    "src/test/ui/const-generics/defaults/const-param-as-default-value.rs",
    "src/test/ui/const-generics/defaults/pretty-printing-ast.rs",
    "src/test/ui/proc-macro/issue-81555.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/closing-args-token.rs",
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2013 Luis de Bethencourt <luis@debethencourt.com>
// Copyright 2013 The Rust Project Developers
// http://rust-lang.org

// 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.

/*!
 * Gregorian Calendar module for the datetime library of the Rust programming
 * language
 *
 * Implements a hybrid calendar that supports both the Julian and Gregorian
 * calendar systems.
 *
 * http://en.wikipedia.org/wiki/Gregorian_calendar
 * http://en.wikipedia.org/wiki/Julian_calendar
 */

static YEARBASE: int = 1900;
static DAYSPERLYEAR: uint = 366;
static DAYSPERNYEAR: uint = 365;
static DAYSPERWEEK: uint = 7;
//static DAYSBEFOREMONTH: ~[~[uint]] = [
static DAYSBEFOREMONTH: [[uint, ..13], ..2] = [
    /* Normal years */
    [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365],
    /* Leap years */
    [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
];

pub fn is_leap_year(year: uint) -> bool {
    (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}

pub fn year_size(year: uint) -> uint {
    if is_leap_year(year) { DAYSPERLYEAR } else { DAYSPERNYEAR }
}

pub struct GCalendar {
    /*
     * Calendar object with date and time.
     */
    sec: uint,         /* Seconds       [0-59]  */
    min: uint,         /* Minutes       [0-59]  */
    hour: uint,        /* Hours         [0-23]  */
    mday: uint,        /* Day           [0-30]  */
    month: uint,       /* Month         [0-11]  */
    year: uint,        /* Year - 1900           */
    wday: uint,        /* Day of week   [0-6]   */
    yday: uint         /* Days in year  [0-365] */
}

impl GCalendar {
    /**
    * Allocates a GCalendar object at epoch.
    */
    pub fn new_at_epoch() -> GCalendar {
        GCalendar {
            sec: 0,
            min: 0,
            hour: 0,
            mday: 0,
            month: 0,
            year: 0,
            wday: 0,
            yday: 0,
        }
    }

    /**
    * Allocates a GCalendar object at the given date and time.
    */
    pub fn new(sec: uint, min: uint, hour: uint, mday: uint, month: uint,
            year: uint, wday: uint, yday: uint) -> GCalendar {
        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            mday: mday,
            month: month,
            year: year,
            wday: wday,
            yday: yday,
        }
    }

    /**
    * Allocates a GCalendar object from the milliseconds elapsed since epoch.
    */
    pub fn new_from_epoch(since_epoch: uint) -> GCalendar {
        let epoch_year = 1970;
        let mut year = epoch_year;

        let millisecs_day = 86400000;

        let mut dayclock = since_epoch % millisecs_day;
        let mut dayno = since_epoch / millisecs_day;

        let hour = dayclock / 3600000;
        dayclock = dayclock - (hour * 3600000);
        let min = dayclock / 60000;
        dayclock = dayclock - (min * 60000);
        let sec = dayclock / 1000;
        let wday = (dayno + 4) % 7;

        while (dayno >= year_size(year)) {
            dayno -= year_size(year);
            year += 1;
        }
        let yday = dayno;

        let ip = DAYSBEFOREMONTH[if is_leap_year(year) {1} else {0}];
        let mut month = 11;
        while (dayno < ip[month]) {
            month -= 1;
        }
        dayno -= ip[month];

        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            mday: dayno + 1,
            month: month  + 1,
            year: year,
            wday: wday,
            yday: yday,
        }
    }

    pub fn get_sec(&self) -> uint {
        self.sec
    }

    pub fn get_min(&self) -> uint {
        self.min
    }

    pub fn get_hour(&self) -> uint {
        self.hour
    }

    pub fn get_day_of_month(&self) -> uint {
        self.mday
    }

    pub fn get_month(&self) -> uint {
        self.month
    }

    pub fn get_year(&self) -> uint {
        self.year
    }

    pub fn get_day_of_week(&self) -> uint {
        self.wday
    }

    pub fn get_day_of_year(&self) -> uint {
        self.yday
    }

    pub fn get_date(&self) -> ~str {
        // ToDo
        ~""
    }
}

#[cfg(test)]
mod test {
    use super::GCalendar;

    #[test]
    fn new() {
        let gc = GCalendar::new(21, 0, 12, 23, 9, 1983, 5, 265);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_day_of_month(), 23);
        assert_eq!(gc.get_month(), 9);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_week(), 5);
        assert_eq!(gc.get_day_of_year(), 265);
    }

    #[test]
    fn new_from_epoch() {
        let gc = GCalendar::new_from_epoch(433166421023);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_day_of_month(), 23);
        assert_eq!(gc.get_month(), 9);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_week(), 5);
        assert_eq!(gc.get_day_of_year(), 265);
    }
}
<commit_msg>gcalendar.rs: add get_date()<commit_after>// Copyright 2013 Luis de Bethencourt <luis@debethencourt.com>
// Copyright 2013 The Rust Project Developers
// http://rust-lang.org

// 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.

/*!
 * Gregorian Calendar module for the datetime library of the Rust programming
 * language
 *
 * Implements a hybrid calendar that supports both the Julian and Gregorian
 * calendar systems.
 *
 * http://en.wikipedia.org/wiki/Gregorian_calendar
 * http://en.wikipedia.org/wiki/Julian_calendar
 */

static YEARBASE: int = 1900;
static DAYSPERLYEAR: uint = 366;
static DAYSPERNYEAR: uint = 365;
static DAYSPERWEEK: uint = 7;
//static DAYSBEFOREMONTH: ~[~[uint]] = [
static DAYSBEFOREMONTH: [[uint, ..13], ..2] = [
    /* Normal years */
    [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365],
    /* Leap years */
    [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
];

pub fn is_leap_year(year: uint) -> bool {
    (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}

pub fn year_size(year: uint) -> uint {
    if is_leap_year(year) { DAYSPERLYEAR } else { DAYSPERNYEAR }
}

pub struct GCalendar {
    /*
     * Calendar object with date and time.
     */
    sec: uint,         /* Seconds       [0-59]  */
    min: uint,         /* Minutes       [0-59]  */
    hour: uint,        /* Hours         [0-23]  */
    mday: uint,        /* Day           [0-30]  */
    month: uint,       /* Month         [0-11]  */
    year: uint,        /* Year - 1900           */
    wday: uint,        /* Day of week   [0-6]   */
    yday: uint         /* Days in year  [0-365] */
}

impl GCalendar {
    /**
    * Allocates a GCalendar object at epoch.
    */
    pub fn new_at_epoch() -> GCalendar {
        GCalendar {
            sec: 0,
            min: 0,
            hour: 0,
            mday: 0,
            month: 0,
            year: 0,
            wday: 0,
            yday: 0,
        }
    }

    /**
    * Allocates a GCalendar object at the given date and time.
    */
    pub fn new(sec: uint, min: uint, hour: uint, mday: uint, month: uint,
            year: uint, wday: uint, yday: uint) -> GCalendar {
        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            mday: mday,
            month: month,
            year: year,
            wday: wday,
            yday: yday,
        }
    }

    /**
    * Allocates a GCalendar object from the milliseconds elapsed since epoch.
    */
    pub fn new_from_epoch(since_epoch: uint) -> GCalendar {
        let epoch_year = 1970;
        let mut year = epoch_year;

        let millisecs_day = 86400000;

        let mut dayclock = since_epoch % millisecs_day;
        let mut dayno = since_epoch / millisecs_day;

        let hour = dayclock / 3600000;
        dayclock = dayclock - (hour * 3600000);
        let min = dayclock / 60000;
        dayclock = dayclock - (min * 60000);
        let sec = dayclock / 1000;
        let wday = (dayno + 4) % 7;

        while (dayno >= year_size(year)) {
            dayno -= year_size(year);
            year += 1;
        }
        let yday = dayno;

        let ip = DAYSBEFOREMONTH[if is_leap_year(year) {1} else {0}];
        let mut month = 11;
        while (dayno < ip[month]) {
            month -= 1;
        }
        dayno -= ip[month];

        GCalendar {
            sec: sec,
            min: min,
            hour: hour,
            mday: dayno + 1,
            month: month  + 1,
            year: year,
            wday: wday,
            yday: yday,
        }
    }

    pub fn get_sec(&self) -> uint {
        self.sec
    }

    pub fn get_min(&self) -> uint {
        self.min
    }

    pub fn get_hour(&self) -> uint {
        self.hour
    }

    pub fn get_day_of_month(&self) -> uint {
        self.mday
    }

    pub fn get_month(&self) -> uint {
        self.month
    }

    pub fn get_year(&self) -> uint {
        self.year
    }

    pub fn get_day_of_week(&self) -> uint {
        self.wday
    }

    pub fn get_day_of_year(&self) -> uint {
        self.yday
    }

    pub fn get_date(&self) -> ~str {
        // Formatting date and time using the ISO 8601
        // 1970-01-01T00:00:00Z
        fmt!("%04u-%02u-%02uT%02u:%02u:%02uZ", self.year, self.month, self.mday,
                self.hour, self.min, self.sec)
    }
}

#[cfg(test)]
mod test {
    use super::GCalendar;

    #[test]
    fn new() {
        let gc = GCalendar::new(21, 0, 12, 23, 9, 1983, 5, 265);
        assert_eq!(gc.get_sec(), 21);
        assert_eq!(gc.get_min(), 0);
        assert_eq!(gc.get_hour(), 12);
        assert_eq!(gc.get_day_of_month(), 23);
        assert_eq!(gc.get_month(), 9);
        assert_eq!(gc.get_year(), 1983);
        assert_eq!(gc.get_day_of_week(), 5);
        assert_eq!(gc.get_day_of_year(), 265);
    }

    #[test]
    fn new_from_epoch() {
        let gc = GCalendar::new_from_epoch(433166421023);
        assert_eq!(gc.get_day_of_week(), 5);
        assert_eq!(gc.get_day_of_year(), 265);
        assert_eq!(gc.get_date(), ~"1983-09-23T12:00:21Z");
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate plank_errors;
extern crate plank_syntax;
extern crate plank_frontend;
extern crate plank_ir;
extern crate plank_interpreter;

mod test_parser;

use std::fs;
use std::io;
use std::io::prelude::*;
use plank_errors::reporter::Diagnostic;

fn build_code(source: &str) -> Result<plank_ir::Program, Vec<Diagnostic>> {
    let reporter = plank_errors::Reporter::new();
    let tokens = plank_syntax::lex(source, reporter.clone());
    let program = plank_syntax::parse(tokens, reporter.clone());
    plank_frontend::compile(&program, reporter.clone()).map_err(|()| {
        reporter.get_diagnostics()
    })
}

#[derive(Debug)]
enum TestResult {
    BuildFail(Vec<Diagnostic>),
    BadBuildPass(Vec<test_parser::Error>),
    BuildErrorMismatch(Vec<Diagnostic>, Vec<test_parser::Error>),
    IoMismatch { expected: Vec<u8>, got: Vec<u8> },
    MalformedTest(test_parser::ParseError),
    InterpreterExit(i32),
    InterpreterError(plank_interpreter::Error),
    Ok,
}

fn match_build_errors(expected: Vec<test_parser::Error>, got: Vec<Diagnostic>) -> TestResult {
    let mut unmatched = Vec::new();
    for err in expected {
        let mut matched = false;
        for actual in &got {
            if let Some(span) = actual.primary_span {
                if span.start.line == err.line && actual.message.contains(&err.message) {
                    matched = true;
                    break;
                }
            }
        }
        if !matched {
            unmatched.push(err);
        }
    }
    if unmatched.is_empty() {
        TestResult::Ok
    } else {
        TestResult::BuildErrorMismatch(got, unmatched)
    }
}

fn interpret_program(program: plank_ir::Program, input: Vec<u8>, output: Vec<u8>) -> TestResult {
    let mut input = ::std::io::Cursor::new(input);
    let mut actual_output = Vec::new();
    match plank_interpreter::run_program(&program, &mut input, &mut actual_output) {
        Ok(0) if actual_output == output => TestResult::Ok,
        Ok(0) => TestResult::IoMismatch { expected: output, got: actual_output },
        Ok(code) => TestResult::InterpreterExit(code),
        Err(e) => TestResult::InterpreterError(e),
    }
}

fn run_test(source: &str) -> TestResult {
    let expectation = match test_parser::parse_test(source) {
        Ok(e) => e,
        Err(e) => return TestResult::MalformedTest(e),
    };
    match expectation {
        test_parser::Expectation::BuildErrors(errors) => {
            match build_code(source) {
                Ok(_) => TestResult::BadBuildPass(errors),
                Err(got) => match_build_errors(errors, got),
            }
        }
        test_parser::Expectation::Io { input, output} => {
            match build_code(source) {
                Ok(program) => interpret_program(program, input, output),
                Err(e) => TestResult::BuildFail(e),
            }
        }
        test_parser::Expectation::BuildSuccess => {
            match build_code(source) {
                Ok(_) => TestResult::Ok,
                Err(e) => TestResult::BuildFail(e),
            }
        }
    }
}

fn run_tests() -> io::Result<Vec<(String, String, TestResult)>> {
    let mut test_results = Vec::new();
    for dir in TEST_DIRS {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if !entry.file_type()?.is_file() {
                continue;
            }
            let mut file = fs::File::open(entry.path())?;
            let mut source = String::new();
            file.read_to_string(&mut source)?;
            let test_result = run_test(&source);
            let test_name = entry.path().display().to_string();
            if let TestResult::Ok = test_result {
                println!("test {} ... ok", test_name);
            } else {
                println!("test {} ... FAIL", test_name);
            }
            test_results.push((test_name, source, test_result));
        }
    }
    Ok(test_results)
}

fn print_expected_errors(errors: &[test_parser::Error]) {
    for err in errors {
        println!("at line {}: {}", err.line + 1, err.message);
    }
}

fn print_output(out: &[u8]) {
    for &byte in out {
        if byte < 32 || byte >= 127 || byte == b'\\' {
            print!("\\x{:>02X}", byte);
        } else {
            print!("{}", byte as char);
        }
    }
    println!();
}

fn report_results(results: &[(String, String, TestResult)]) {
    let mut passed = 0;
    for &(ref name, ref source, ref result) in results {
        match *result {
            TestResult::Ok => passed += 1,
            TestResult::BuildFail(ref diagnostics) => {
                println!("========================================");
                println!("test {}", name);
                println!("unexpected build failure");
                plank_errors::printer::print_diagnostics(source, diagnostics);
                println!();
            }
            TestResult::BadBuildPass(ref expected) => {
                println!("========================================");
                println!("test {}", name);
                println!("build passed but expected failure");
                print_expected_errors(expected);
            }
            TestResult::BuildErrorMismatch(ref got, ref expected) => {
                println!("========================================");
                println!("test {}", name);
                println!("build error mismatch");
                println!(">> unmatched errors:");
                print_expected_errors(expected);
                println!(">> actual errors:");
                plank_errors::printer::print_diagnostics(source, got);
                println!();
            }
            TestResult::IoMismatch { ref expected, ref got } => {
                println!("========================================");
                println!("test {}", name);
                println!("wrong output");
                print!("Expected: ");
                print_output(expected);
                print!("Got:      ");
                print_output(got);
                println!();
            }
            TestResult::MalformedTest(ref err) => {
                println!("========================================");
                println!("test {}", name);
                println!("malformed test");
                println!("{}", err);
                println!();
            }
            TestResult::InterpreterExit(code) => {
                println!("========================================");
                println!("test {}", name);
                println!("interpreted program exited with code {}", code);
                println!();
            }
            TestResult::InterpreterError(ref err) => {
                println!("========================================");
                println!("test {}", name);
                println!("interpreted program crashed");
                match *err {
                    plank_interpreter::Error::BadDeref => {
                        println!("invalid memory access");
                    }
                    plank_interpreter::Error::DivisionByZero => {
                        println!("division by zero");
                    }
                    plank_interpreter::Error::MissingSymbol(_) => {
                        println!("missing symbol definition");
                    }
                    plank_interpreter::Error::Io(ref err) => {
                        println!("io error: {}", err);
                    }
                }
                println!();
            }
        }
    }
    println!("========================================");
    println!("tests:  {}", results.len());
    println!("passed: {}", passed);
    println!("failed: {}", results.len() - passed);
}

fn main() {
    match run_tests() {
        Ok(results) => report_results(&results),
        Err(e) => println!("io error: {}", e),
    }
}

const TEST_DIRS: &'static [&'static str] = &[
    // test examples because obviously we want the
    // examples people come accross to build successfully
    "./examples",
    "./tests/compile-fail",
    "./tests/pass",
];
<commit_msg>no longer allow dead code<commit_after>extern crate plank_errors;
extern crate plank_syntax;
extern crate plank_frontend;
extern crate plank_ir;
extern crate plank_interpreter;

mod test_parser;

use std::fs;
use std::io;
use std::io::prelude::*;
use plank_errors::reporter::Diagnostic;

fn build_code(source: &str) -> Result<plank_ir::Program, Vec<Diagnostic>> {
    let reporter = plank_errors::Reporter::new();
    let tokens = plank_syntax::lex(source, reporter.clone());
    let program = plank_syntax::parse(tokens, reporter.clone());
    plank_frontend::compile(&program, reporter.clone()).map_err(|()| {
        reporter.get_diagnostics()
    })
}

#[derive(Debug)]
enum TestResult {
    BuildFail(Vec<Diagnostic>),
    BadBuildPass(Vec<test_parser::Error>),
    BuildErrorMismatch(Vec<Diagnostic>, Vec<test_parser::Error>),
    IoMismatch { expected: Vec<u8>, got: Vec<u8> },
    MalformedTest(test_parser::ParseError),
    InterpreterExit(i32),
    InterpreterError(plank_interpreter::Error),
    Ok,
}

fn match_build_errors(expected: Vec<test_parser::Error>, got: Vec<Diagnostic>) -> TestResult {
    let mut unmatched = Vec::new();
    for err in expected {
        let mut matched = false;
        for actual in &got {
            if let Some(span) = actual.primary_span {
                if span.start.line == err.line && actual.message.contains(&err.message) {
                    matched = true;
                    break;
                }
            }
        }
        if !matched {
            unmatched.push(err);
        }
    }
    if unmatched.is_empty() {
        TestResult::Ok
    } else {
        TestResult::BuildErrorMismatch(got, unmatched)
    }
}

fn interpret_program(program: plank_ir::Program, input: Vec<u8>, output: Vec<u8>) -> TestResult {
    let mut input = ::std::io::Cursor::new(input);
    let mut actual_output = Vec::new();
    match plank_interpreter::run_program(&program, &mut input, &mut actual_output) {
        Ok(0) if actual_output == output => TestResult::Ok,
        Ok(0) => TestResult::IoMismatch { expected: output, got: actual_output },
        Ok(code) => TestResult::InterpreterExit(code),
        Err(e) => TestResult::InterpreterError(e),
    }
}

fn run_test(source: &str) -> TestResult {
    let expectation = match test_parser::parse_test(source) {
        Ok(e) => e,
        Err(e) => return TestResult::MalformedTest(e),
    };
    match expectation {
        test_parser::Expectation::BuildErrors(errors) => {
            match build_code(source) {
                Ok(_) => TestResult::BadBuildPass(errors),
                Err(got) => match_build_errors(errors, got),
            }
        }
        test_parser::Expectation::Io { input, output} => {
            match build_code(source) {
                Ok(program) => interpret_program(program, input, output),
                Err(e) => TestResult::BuildFail(e),
            }
        }
        test_parser::Expectation::BuildSuccess => {
            match build_code(source) {
                Ok(_) => TestResult::Ok,
                Err(e) => TestResult::BuildFail(e),
            }
        }
    }
}

fn run_tests() -> io::Result<Vec<(String, String, TestResult)>> {
    let mut test_results = Vec::new();
    for dir in TEST_DIRS {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            if !entry.file_type()?.is_file() {
                continue;
            }
            let mut file = fs::File::open(entry.path())?;
            let mut source = String::new();
            file.read_to_string(&mut source)?;
            let test_result = run_test(&source);
            let test_name = entry.path().display().to_string();
            if let TestResult::Ok = test_result {
                println!("test {} ... ok", test_name);
            } else {
                println!("test {} ... FAIL", test_name);
            }
            test_results.push((test_name, source, test_result));
        }
    }
    Ok(test_results)
}

fn print_expected_errors(errors: &[test_parser::Error]) {
    for err in errors {
        println!("at line {}: {}", err.line + 1, err.message);
    }
}

fn print_output(out: &[u8]) {
    for &byte in out {
        if byte < 32 || byte >= 127 || byte == b'\\' {
            print!("\\x{:>02X}", byte);
        } else {
            print!("{}", byte as char);
        }
    }
    println!();
}

fn report_results(results: &[(String, String, TestResult)]) {
    let mut passed = 0;
    for &(ref name, ref source, ref result) in results {
        match *result {
            TestResult::Ok => passed += 1,
            TestResult::BuildFail(ref diagnostics) => {
                println!("========================================");
                println!("test {}", name);
                println!("unexpected build failure");
                plank_errors::printer::print_diagnostics(source, diagnostics);
                println!();
            }
            TestResult::BadBuildPass(ref expected) => {
                println!("========================================");
                println!("test {}", name);
                println!("build passed but expected failure");
                print_expected_errors(expected);
            }
            TestResult::BuildErrorMismatch(ref got, ref expected) => {
                println!("========================================");
                println!("test {}", name);
                println!("build error mismatch");
                println!(">> unmatched errors:");
                print_expected_errors(expected);
                println!(">> actual errors:");
                plank_errors::printer::print_diagnostics(source, got);
                println!();
            }
            TestResult::IoMismatch { ref expected, ref got } => {
                println!("========================================");
                println!("test {}", name);
                println!("wrong output");
                print!("Expected: ");
                print_output(expected);
                print!("Got:      ");
                print_output(got);
                println!();
            }
            TestResult::MalformedTest(ref err) => {
                println!("========================================");
                println!("test {}", name);
                println!("malformed test");
                println!("{}", err);
                println!();
            }
            TestResult::InterpreterExit(code) => {
                println!("========================================");
                println!("test {}", name);
                println!("interpreted program exited with code {}", code);
                println!();
            }
            TestResult::InterpreterError(ref err) => {
                println!("========================================");
                println!("test {}", name);
                println!("interpreted program crashed");
                match *err {
                    plank_interpreter::Error::BadDeref => {
                        println!("invalid memory access");
                    }
                    plank_interpreter::Error::DivisionByZero => {
                        println!("division by zero");
                    }
                    plank_interpreter::Error::MissingSymbol(_) => {
                        println!("missing symbol definition");
                    }
                    plank_interpreter::Error::Io(ref err) => {
                        println!("io error: {}", err);
                    }
                }
                println!();
            }
        }
    }
    println!("========================================");
    println!("tests:  {}", results.len());
    println!("passed: {}", passed);
    println!("failed: {}", results.len() - passed);
}

fn main() {
    match run_tests() {
        Ok(results) => report_results(&results),
        Err(e) => println!("io error: {}", e),
    }
}

const TEST_DIRS: &'static [&'static str] = &[
    // test examples because obviously we want the
    // examples people come accross to build successfully
    "./examples",
    "./tests/compile-fail",
    "./tests/pass",
];
<|endoftext|>


<commit_before>mod no_generics {
    struct Ty;

    type A = Ty;

    type B = Ty<'static>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| HELP remove these generics

    type C = Ty<'static, usize>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| ERROR this struct takes 0 generic arguments but 1 generic argument
    //~| HELP remove this lifetime argument
    //~| HELP remove this generic argument

    type D = Ty<'static, usize, { 0 }>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| ERROR this struct takes 0 generic arguments but 2 generic arguments
    //~| HELP remove this lifetime argument
    //~| HELP remove these generic arguments
}

mod type_and_type {
    struct Ty<A, B>;

    type A = Ty;
    //~^ ERROR missing generics for struct `type_and_type::Ty`
    //~| HELP add missing

    type B = Ty<usize>;
    //~^ ERROR this struct takes 2 generic arguments but 1 generic argument
    //~| HELP add missing

    type C = Ty<usize, String>;

    type D = Ty<usize, String, char>;
    //~^ ERROR this struct takes 2 generic arguments but 3 generic arguments
    //~| HELP remove this
}

mod lifetime_and_type {
    struct Ty<'a, T>;

    type A = Ty;
    //~^ ERROR missing generics for struct
    //~| ERROR missing lifetime specifier
    //~| HELP add missing
    //~| HELP consider introducing

    type B = Ty<'static>;
    //~^ ERROR this struct takes 1 generic argument but 0 generic arguments
    //~| HELP add missing

    type C = Ty<usize>;
    //~^ ERROR missing lifetime specifier
    //~| HELP consider introducing

    type D = Ty<'static, usize>;
}

mod type_and_type_and_type {
    struct Ty<A, B, C = &'static str>;

    type A = Ty;
    //~^ ERROR missing generics for struct `type_and_type_and_type::Ty`
    //~| HELP add missing

    type B = Ty<usize>;
    //~^ ERROR this struct takes at least 2
    //~| HELP add missing

    type C = Ty<usize, String>;

    type D = Ty<usize, String, char>;

    type E = Ty<usize, String, char, f64>;
    //~^ ERROR this struct takes at most 3
    //~| HELP remove
}

// Traits have an implicit `Self` type - these tests ensure we don't accidentally return it
// somewhere in the message
mod r#trait {
    trait NonGeneric {
        //
    }

    trait GenericLifetime<'a> {
        //
    }

    trait GenericType<A> {
        //
    }

    type A = Box<dyn NonGeneric<usize>>;
    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
    //~| HELP remove

    type B = Box<dyn GenericLifetime>;
    //~^ ERROR missing lifetime specifier
    //~| HELP consider introducing

    type C = Box<dyn GenericLifetime<'static, 'static>>;
    //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
    //~| HELP remove

    type D = Box<dyn GenericType>;
    //~^ ERROR missing generics for trait `GenericType`
    //~| HELP add missing

    type E = Box<dyn GenericType<String, usize>>;
    //~^ ERROR this trait takes 1 generic argument but 2 generic arguments
    //~| HELP remove
}

mod stdlib {
    mod hash_map {
        use std::collections::HashMap;

        type A = HashMap;
        //~^ ERROR missing generics for struct `HashMap`
        //~| HELP add missing

        type B = HashMap<String>;
        //~^ ERROR this struct takes at least
        //~| HELP add missing

        type C = HashMap<'static>;
        //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
        //~| HELP remove these generics
        //~| ERROR this struct takes at least 2
        //~| HELP add missing

        type D = HashMap<usize, String, char, f64>;
        //~^ ERROR this struct takes at most 3
        //~| HELP remove this
    }

    mod result {
        type A = Result;
        //~^ ERROR missing generics for enum `Result`
        //~| HELP add missing

        type B = Result<String>;
        //~^ ERROR this enum takes 2 generic arguments but 1 generic argument
        //~| HELP add missing

        type C = Result<'static>;
        //~^ ERROR this enum takes 0 lifetime arguments but 1 lifetime argument
        //~| HELP remove these generics
        //~| ERROR this enum takes 2 generic arguments but 0 generic arguments
        //~| HELP add missing

        type D = Result<usize, String, char>;
        //~^ ERROR this enum takes 2 generic arguments but 3 generic arguments
        //~| HELP remove
    }
}

fn main() { }
<commit_msg>Update wrong-number-of-args test to cover more edge cases<commit_after>mod no_generics {
    struct Ty;

    type A = Ty;

    type B = Ty<'static>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| HELP remove these generics

    type C = Ty<'static, usize>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| ERROR this struct takes 0 generic arguments but 1 generic argument
    //~| HELP remove this lifetime argument
    //~| HELP remove this generic argument

    type D = Ty<'static, usize, { 0 }>;
    //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
    //~| ERROR this struct takes 0 generic arguments but 2 generic arguments
    //~| HELP remove this lifetime argument
    //~| HELP remove these generic arguments
}

mod type_and_type {
    struct Ty<A, B>;

    type A = Ty;
    //~^ ERROR missing generics for struct `type_and_type::Ty`
    //~| HELP add missing

    type B = Ty<usize>;
    //~^ ERROR this struct takes 2 generic arguments but 1 generic argument
    //~| HELP add missing

    type C = Ty<usize, String>;

    type D = Ty<usize, String, char>;
    //~^ ERROR this struct takes 2 generic arguments but 3 generic arguments
    //~| HELP remove this

    type E = Ty<>;
    //~^ ERROR this struct takes 2 generic arguments but 0 generic arguments were supplied
    //~| HELP add missing
}

mod lifetime_and_type {
    struct Ty<'a, T>;

    type A = Ty;
    //~^ ERROR missing generics for struct
    //~| ERROR missing lifetime specifier
    //~| HELP add missing
    //~| HELP consider introducing

    type B = Ty<'static>;
    //~^ ERROR this struct takes 1 generic argument but 0 generic arguments
    //~| HELP add missing

    type C = Ty<usize>;
    //~^ ERROR missing lifetime specifier
    //~| HELP consider introducing

    type D = Ty<'static, usize>;

    type E = Ty<>;
    //~^ ERROR this struct takes 1 generic argument but 0 generic arguments
    //~| ERROR missing lifetime specifier
    //~| HELP consider introducing
    //~| HELP add missing
}

mod type_and_type_and_type {
    struct Ty<A, B, C = &'static str>;

    type A = Ty;
    //~^ ERROR missing generics for struct `type_and_type_and_type::Ty`
    //~| HELP add missing

    type B = Ty<usize>;
    //~^ ERROR this struct takes at least 2
    //~| HELP add missing

    type C = Ty<usize, String>;

    type D = Ty<usize, String, char>;

    type E = Ty<usize, String, char, f64>;
    //~^ ERROR this struct takes at most 3
    //~| HELP remove

    type F = Ty<>;
    //~^ ERROR this struct takes at least 2 generic arguments but 0 generic arguments
    //~| HELP add missing
}

// Traits have an implicit `Self` type - these tests ensure we don't accidentally return it
// somewhere in the message
mod r#trait {
    trait NonGeneric {
        //
    }

    trait GenericLifetime<'a> {
        //
    }

    trait GenericType<A> {
        //
    }

    type A = Box<dyn NonGeneric<usize>>;
    //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
    //~| HELP remove

    type B = Box<dyn GenericLifetime>;
    //~^ ERROR missing lifetime specifier
    //~| HELP consider introducing

    type C = Box<dyn GenericLifetime<'static, 'static>>;
    //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
    //~| HELP remove

    type D = Box<dyn GenericType>;
    //~^ ERROR missing generics for trait `GenericType`
    //~| HELP add missing

    type E = Box<dyn GenericType<String, usize>>;
    //~^ ERROR this trait takes 1 generic argument but 2 generic arguments
    //~| HELP remove

    type F = Box<dyn GenericLifetime<>>;
    //~^ ERROR missing lifetime specifier
    //~| HELP consider introducing

    type G = Box<dyn GenericType<>>;
    //~^ ERROR this trait takes 1 generic argument but 0 generic arguments
    //~| HELP add missing
}

mod associated_item {
    mod non_generic {
        trait NonGenericAT {
            type AssocTy;
        }

        type A = Box<dyn NonGenericAT<usize, AssocTy=()>>;
        //~^ ERROR this trait takes 0 generic arguments but 1 generic argument
        //~| HELP remove
    }

    mod lifetime {
        trait GenericLifetimeAT<'a> {
            type AssocTy;
        }

        type A = Box<dyn GenericLifetimeAT<AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing

        type B = Box<dyn GenericLifetimeAT<'static, 'static, AssocTy=()>>;
        //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
        //~| HELP remove

        type C = Box<dyn GenericLifetimeAT<(), AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing
        //~| ERROR this trait takes 0 generic arguments but 1 generic argument
        //~| HELP remove
    }

    mod r#type {
        trait GenericTypeAT<A> {
            type AssocTy;
        }

        type A = Box<dyn GenericTypeAT<AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing

        type B = Box<dyn GenericTypeAT<(), (), AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 2 generic arguments
        //~| HELP remove

        type C = Box<dyn GenericTypeAT<'static, AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing
        //~| ERROR this trait takes 0 lifetime arguments but 1 lifetime argument was supplied
        //~| HELP remove
    }

    mod lifetime_and_type {
        trait GenericLifetimeTypeAT<'a, A> {
            type AssocTy;
        }

        type A = Box<dyn GenericLifetimeTypeAT<AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing
        //~| ERROR missing lifetime specifier
        //~| HELP consider introducing

        type B = Box<dyn GenericLifetimeTypeAT<'static, AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 0 generic arguments were supplied
        //~| HELP add missing

        type C = Box<dyn GenericLifetimeTypeAT<'static, 'static, AssocTy=()>>;
        //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
        //~| HELP remove
        //~| ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing

        type D = Box<dyn GenericLifetimeTypeAT<(), AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing

        type E = Box<dyn GenericLifetimeTypeAT<(), (), AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing
        //~| ERROR this trait takes 1 generic argument but 2 generic arguments
        //~| HELP remove

        type F = Box<dyn GenericLifetimeTypeAT<'static, 'static, (), AssocTy=()>>;
        //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
        //~| HELP remove

        type G = Box<dyn GenericLifetimeTypeAT<'static, (), (), AssocTy=()>>;
        //~^ ERROR this trait takes 1 generic argument but 2 generic arguments
        //~| HELP remove

        type H = Box<dyn GenericLifetimeTypeAT<'static, 'static, (), (), AssocTy=()>>;
        //~^ ERROR this trait takes 1 lifetime argument but 2 lifetime arguments were supplied
        //~| HELP remove
        //~| ERROR this trait takes 1 generic argument but 2 generic arguments
        //~| HELP remove
    }

    mod type_and_type {
        trait GenericTypeTypeAT<A, B> {
            type AssocTy;
        }

        type A = Box<dyn GenericTypeTypeAT<AssocTy=()>>;
        //~^ ERROR this trait takes 2 generic arguments but 0 generic arguments
        //~| HELP add missing

        type B = Box<dyn GenericTypeTypeAT<(), AssocTy=()>>;
        //~^ ERROR this trait takes 2 generic arguments but 1 generic argument
        //~| HELP add missing

        type C = Box<dyn GenericTypeTypeAT<(), (), (), AssocTy=()>>;
        //~^ ERROR this trait takes 2 generic arguments but 3 generic arguments
        //~| HELP remove
    }

    mod lifetime_and_lifetime {
        trait GenericLifetimeLifetimeAT<'a, 'b> {
            type AssocTy;
        }

        type A = Box<dyn GenericLifetimeLifetimeAT<AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing

        type B = Box<dyn GenericLifetimeLifetimeAT<'static, AssocTy=()>>;
        //~^ ERROR this trait takes 2 lifetime arguments but 1 lifetime argument was supplied
        //~| HELP add missing lifetime argument
    }

    mod lifetime_and_lifetime_and_type {
        trait GenericLifetimeLifetimeTypeAT<'a, 'b, A> {
            type AssocTy;
        }

        type A = Box<dyn GenericLifetimeLifetimeTypeAT<AssocTy=()>>;
        //~^ ERROR missing lifetime specifier
        //~| HELP consider introducing
        //~| ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing

        type B = Box<dyn GenericLifetimeLifetimeTypeAT<'static, AssocTy=()>>;
        //~^ ERROR this trait takes 2 lifetime arguments but 1 lifetime argument was supplied
        //~| HELP add missing lifetime argument
        //~| ERROR this trait takes 1 generic argument but 0 generic arguments
        //~| HELP add missing

        type C = Box<dyn GenericLifetimeLifetimeTypeAT<'static, (), AssocTy=()>>;
        //~^ ERROR this trait takes 2 lifetime arguments but 1 lifetime argument was supplied
        //~| HELP add missing lifetime argument
    }
}

mod stdlib {
    mod hash_map {
        use std::collections::HashMap;

        type A = HashMap;
        //~^ ERROR missing generics for struct `HashMap`
        //~| HELP add missing

        type B = HashMap<String>;
        //~^ ERROR this struct takes at least
        //~| HELP add missing

        type C = HashMap<'static>;
        //~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument
        //~| HELP remove these generics
        //~| ERROR this struct takes at least 2
        //~| HELP add missing

        type D = HashMap<usize, String, char, f64>;
        //~^ ERROR this struct takes at most 3
        //~| HELP remove this

        type E = HashMap<>;
        //~^ ERROR this struct takes at least 2 generic arguments but 0 generic arguments
        //~| HELP add missing
    }

    mod result {
        type A = Result;
        //~^ ERROR missing generics for enum `Result`
        //~| HELP add missing

        type B = Result<String>;
        //~^ ERROR this enum takes 2 generic arguments but 1 generic argument
        //~| HELP add missing

        type C = Result<'static>;
        //~^ ERROR this enum takes 0 lifetime arguments but 1 lifetime argument
        //~| HELP remove these generics
        //~| ERROR this enum takes 2 generic arguments but 0 generic arguments
        //~| HELP add missing

        type D = Result<usize, String, char>;
        //~^ ERROR this enum takes 2 generic arguments but 3 generic arguments
        //~| HELP remove

        type E = Result<>;
        //~^ ERROR this enum takes 2 generic arguments but 0 generic arguments
        //~| HELP add missing
    }
}

fn main() { }
<|endoftext|>


<commit_before>// Copyright 2012-2014 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.

#![allow(non_camel_case_types)]

use std::cell::{RefCell, Cell};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::repeat;
use std::time::Duration;
use std::collections::hash_state::HashState;

use syntax::ast;
use syntax::visit;
use syntax::visit::Visitor;

// The name of the associated type for `Fn` return types
pub const FN_OUTPUT_NAME: &'static str = "Output";

// Useful type to use with `Result<>` indicate that an error has already
// been reported to the user, so no need to continue checking.
#[derive(Clone, Copy, Debug)]
pub struct ErrorReported;

pub fn time<T, U, F>(do_it: bool, what: &str, u: U, f: F) -> T where
    F: FnOnce(U) -> T,
{
    thread_local!(static DEPTH: Cell<uint> = Cell::new(0));
    if !do_it { return f(u); }

    let old = DEPTH.with(|slot| {
        let r = slot.get();
        slot.set(r + 1);
        r
    });

    let mut u = Some(u);
    let mut rv = None;
    let dur = {
        let ref mut rvp = rv;

        Duration::span(move || {
            *rvp = Some(f(u.take().unwrap()))
        })
    };
    let rv = rv.unwrap();

    println!("{}time: {}.{:03} \t{}", repeat("  ").take(old).collect::<String>(),
             dur.num_seconds(), dur.num_milliseconds() % 1000, what);
    DEPTH.with(|slot| slot.set(old));

    rv
}

pub fn indent<R, F>(op: F) -> R where
    R: Debug,
    F: FnOnce() -> R,
{
    // Use in conjunction with the log post-processor like `src/etc/indenter`
    // to make debug output more readable.
    debug!(">>");
    let r = op();
    debug!("<< (Result = {:?})", r);
    r
}

pub struct Indenter {
    _cannot_construct_outside_of_this_module: ()
}

impl Drop for Indenter {
    fn drop(&mut self) { debug!("<<"); }
}

pub fn indenter() -> Indenter {
    debug!(">>");
    Indenter { _cannot_construct_outside_of_this_module: () }
}

struct LoopQueryVisitor<P> where P: FnMut(&ast::Expr_) -> bool {
    p: P,
    flag: bool,
}

impl<'v, P> Visitor<'v> for LoopQueryVisitor<P> where P: FnMut(&ast::Expr_) -> bool {
    fn visit_expr(&mut self, e: &ast::Expr) {
        self.flag |= (self.p)(&e.node);
        match e.node {
          // Skip inner loops, since a break in the inner loop isn't a
          // break inside the outer loop
          ast::ExprLoop(..) | ast::ExprWhile(..) => {}
          _ => visit::walk_expr(self, e)
        }
    }
}

// Takes a predicate p, returns true iff p is true for any subexpressions
// of b -- skipping any inner loops (loop, while, loop_body)
pub fn loop_query<P>(b: &ast::Block, p: P) -> bool where P: FnMut(&ast::Expr_) -> bool {
    let mut v = LoopQueryVisitor {
        p: p,
        flag: false,
    };
    visit::walk_block(&mut v, b);
    return v.flag;
}

struct BlockQueryVisitor<P> where P: FnMut(&ast::Expr) -> bool {
    p: P,
    flag: bool,
}

impl<'v, P> Visitor<'v> for BlockQueryVisitor<P> where P: FnMut(&ast::Expr) -> bool {
    fn visit_expr(&mut self, e: &ast::Expr) {
        self.flag |= (self.p)(e);
        visit::walk_expr(self, e)
    }
}

// Takes a predicate p, returns true iff p is true for any subexpressions
// of b -- skipping any inner loops (loop, while, loop_body)
pub fn block_query<P>(b: &ast::Block, p: P) -> bool where P: FnMut(&ast::Expr) -> bool {
    let mut v = BlockQueryVisitor {
        p: p,
        flag: false,
    };
    visit::walk_block(&mut v, &*b);
    return v.flag;
}

/// K: Eq + Hash<S>, V, S, H: Hasher<S>
///
/// Determines whether there exists a path from `source` to `destination`.  The
/// graph is defined by the `edges_map`, which maps from a node `S` to a list of
/// its adjacent nodes `T`.
///
/// Efficiency note: This is implemented in an inefficient way because it is
/// typically invoked on very small graphs. If the graphs become larger, a more
/// efficient graph representation and algorithm would probably be advised.
pub fn can_reach<T, S>(edges_map: &HashMap<T, Vec<T>, S>, source: T,
                       destination: T) -> bool
    where S: HashState, T: Hash + Eq + Clone,
{
    if source == destination {
        return true;
    }

    // Do a little breadth-first-search here.  The `queue` list
    // doubles as a way to detect if we've seen a particular FR
    // before.  Note that we expect this graph to be an *extremely
    // shallow* tree.
    let mut queue = vec!(source);
    let mut i = 0;
    while i < queue.len() {
        match edges_map.get(&queue[i]) {
            Some(edges) => {
                for target in edges {
                    if *target == destination {
                        return true;
                    }

                    if !queue.iter().any(|x| x == target) {
                        queue.push((*target).clone());
                    }
                }
            }
            None => {}
        }
        i += 1;
    }
    return false;
}

/// Memoizes a one-argument closure using the given RefCell containing
/// a type implementing MutableMap to serve as a cache.
///
/// In the future the signature of this function is expected to be:
/// ```
/// pub fn memoized<T: Clone, U: Clone, M: MutableMap<T, U>>(
///    cache: &RefCell<M>,
///    f: &|T| -> U
/// ) -> impl |T| -> U {
/// ```
/// but currently it is not possible.
///
/// # Example
/// ```
/// struct Context {
///    cache: RefCell<HashMap<uint, uint>>
/// }
///
/// fn factorial(ctxt: &Context, n: uint) -> uint {
///     memoized(&ctxt.cache, n, |n| match n {
///         0 | 1 => n,
///         _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1)
///     })
/// }
/// ```
#[inline(always)]
pub fn memoized<T, U, S, F>(cache: &RefCell<HashMap<T, U, S>>, arg: T, f: F) -> U
    where T: Clone + Hash + Eq,
          U: Clone,
          S: HashState,
          F: FnOnce(T) -> U,
{
    let key = arg.clone();
    let result = cache.borrow().get(&key).map(|result| result.clone());
    match result {
        Some(result) => result,
        None => {
            let result = f(arg);
            cache.borrow_mut().insert(key, result.clone());
            result
        }
    }
}
<commit_msg>Rollup merge of #22674 - untitaker:issue22243, r=alexcrichton<commit_after>// Copyright 2012-2014 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.

#![allow(non_camel_case_types)]

use std::cell::{RefCell, Cell};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::iter::repeat;
use std::time::Duration;
use std::collections::hash_state::HashState;

use syntax::ast;
use syntax::visit;
use syntax::visit::Visitor;

// The name of the associated type for `Fn` return types
pub const FN_OUTPUT_NAME: &'static str = "Output";

// Useful type to use with `Result<>` indicate that an error has already
// been reported to the user, so no need to continue checking.
#[derive(Clone, Copy, Debug)]
pub struct ErrorReported;

pub fn time<T, U, F>(do_it: bool, what: &str, u: U, f: F) -> T where
    F: FnOnce(U) -> T,
{
    thread_local!(static DEPTH: Cell<uint> = Cell::new(0));
    if !do_it { return f(u); }

    let old = DEPTH.with(|slot| {
        let r = slot.get();
        slot.set(r + 1);
        r
    });

    let mut u = Some(u);
    let mut rv = None;
    let dur = {
        let ref mut rvp = rv;

        Duration::span(move || {
            *rvp = Some(f(u.take().unwrap()))
        })
    };
    let rv = rv.unwrap();

    println!("{}time: {}.{:03} \t{}", repeat("  ").take(old).collect::<String>(),
             dur.num_seconds(), dur.num_milliseconds() % 1000, what);
    DEPTH.with(|slot| slot.set(old));

    rv
}

pub fn indent<R, F>(op: F) -> R where
    R: Debug,
    F: FnOnce() -> R,
{
    // Use in conjunction with the log post-processor like `src/etc/indenter`
    // to make debug output more readable.
    debug!(">>");
    let r = op();
    debug!("<< (Result = {:?})", r);
    r
}

pub struct Indenter {
    _cannot_construct_outside_of_this_module: ()
}

impl Drop for Indenter {
    fn drop(&mut self) { debug!("<<"); }
}

pub fn indenter() -> Indenter {
    debug!(">>");
    Indenter { _cannot_construct_outside_of_this_module: () }
}

struct LoopQueryVisitor<P> where P: FnMut(&ast::Expr_) -> bool {
    p: P,
    flag: bool,
}

impl<'v, P> Visitor<'v> for LoopQueryVisitor<P> where P: FnMut(&ast::Expr_) -> bool {
    fn visit_expr(&mut self, e: &ast::Expr) {
        self.flag |= (self.p)(&e.node);
        match e.node {
          // Skip inner loops, since a break in the inner loop isn't a
          // break inside the outer loop
          ast::ExprLoop(..) | ast::ExprWhile(..) => {}
          _ => visit::walk_expr(self, e)
        }
    }
}

// Takes a predicate p, returns true iff p is true for any subexpressions
// of b -- skipping any inner loops (loop, while, loop_body)
pub fn loop_query<P>(b: &ast::Block, p: P) -> bool where P: FnMut(&ast::Expr_) -> bool {
    let mut v = LoopQueryVisitor {
        p: p,
        flag: false,
    };
    visit::walk_block(&mut v, b);
    return v.flag;
}

struct BlockQueryVisitor<P> where P: FnMut(&ast::Expr) -> bool {
    p: P,
    flag: bool,
}

impl<'v, P> Visitor<'v> for BlockQueryVisitor<P> where P: FnMut(&ast::Expr) -> bool {
    fn visit_expr(&mut self, e: &ast::Expr) {
        self.flag |= (self.p)(e);
        visit::walk_expr(self, e)
    }
}

// Takes a predicate p, returns true iff p is true for any subexpressions
// of b -- skipping any inner loops (loop, while, loop_body)
pub fn block_query<P>(b: &ast::Block, p: P) -> bool where P: FnMut(&ast::Expr) -> bool {
    let mut v = BlockQueryVisitor {
        p: p,
        flag: false,
    };
    visit::walk_block(&mut v, &*b);
    return v.flag;
}

/// K: Eq + Hash<S>, V, S, H: Hasher<S>
///
/// Determines whether there exists a path from `source` to `destination`.  The
/// graph is defined by the `edges_map`, which maps from a node `S` to a list of
/// its adjacent nodes `T`.
///
/// Efficiency note: This is implemented in an inefficient way because it is
/// typically invoked on very small graphs. If the graphs become larger, a more
/// efficient graph representation and algorithm would probably be advised.
pub fn can_reach<T, S>(edges_map: &HashMap<T, Vec<T>, S>, source: T,
                       destination: T) -> bool
    where S: HashState, T: Hash + Eq + Clone,
{
    if source == destination {
        return true;
    }

    // Do a little breadth-first-search here.  The `queue` list
    // doubles as a way to detect if we've seen a particular FR
    // before.  Note that we expect this graph to be an *extremely
    // shallow* tree.
    let mut queue = vec!(source);
    let mut i = 0;
    while i < queue.len() {
        match edges_map.get(&queue[i]) {
            Some(edges) => {
                for target in edges {
                    if *target == destination {
                        return true;
                    }

                    if !queue.iter().any(|x| x == target) {
                        queue.push((*target).clone());
                    }
                }
            }
            None => {}
        }
        i += 1;
    }
    return false;
}

/// Memoizes a one-argument closure using the given RefCell containing
/// a type implementing MutableMap to serve as a cache.
///
/// In the future the signature of this function is expected to be:
/// ```
/// pub fn memoized<T: Clone, U: Clone, M: MutableMap<T, U>>(
///    cache: &RefCell<M>,
///    f: &|T| -> U
/// ) -> impl |T| -> U {
/// ```
/// but currently it is not possible.
///
/// # Example
/// ```
/// struct Context {
///    cache: RefCell<HashMap<uint, uint>>
/// }
///
/// fn factorial(ctxt: &Context, n: uint) -> uint {
///     memoized(&ctxt.cache, n, |n| match n {
///         0 | 1 => n,
///         _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1)
///     })
/// }
/// ```
#[inline(always)]
pub fn memoized<T, U, S, F>(cache: &RefCell<HashMap<T, U, S>>, arg: T, f: F) -> U
    where T: Clone + Hash + Eq,
          U: Clone,
          S: HashState,
          F: FnOnce(T) -> U,
{
    let key = arg.clone();
    let result = cache.borrow().get(&key).cloned();
    match result {
        Some(result) => result,
        None => {
            let result = f(arg);
            cache.borrow_mut().insert(key, result.clone());
            result
        }
    }
}
<|endoftext|>