<commit_before>/*! Hex map display demo */

extern crate num;
extern crate rustc_serialize;
extern crate image;
extern crate tiled;

#[macro_use] extern crate calx_ecs;
extern crate calx;

mod cmd;
mod render;
mod spr;
mod world;

use std::convert::{Into};
use std::collections::{HashSet};
use calx_ecs::{Entity};
use calx::backend::{CanvasBuilder, CanvasUtil, Event, MouseButton, Key};
use calx::{V2, Rect, Rgba, color, Dir6};
use calx::{Projection, Kernel, KernelTerrain};

use spr::Spr;
use render::RenderTerrain;
use world::World;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Terrain {
    Floor,
    Grass,
    Water,
    Tree,
    Wall,
    Door,
    Window,
    Magma,
    Rock,
    Void,
}

impl Terrain {
    pub fn new(id: u8) -> Terrain {
        // Tiled indexes start from 1.
        let id = id - 1;
        assert!(id <= Terrain::Rock as u8);
        unsafe {
            std::mem::transmute(id)
        }
    }

    pub fn can_walk(self) -> bool {
        use Terrain::*;
        match self {
            Floor | Grass | Door => true,
            _ => false,
        }
    }
}

impl KernelTerrain for Terrain {
    fn is_wall(&self) -> bool {
        use Terrain::*;
        match *self {
            Wall | Door | Window => true,
            _ => false
        }
    }

    fn is_block(&self) -> bool { *self == Terrain::Rock }
}

pub struct Sprite {
    pub spr: Spr,
    pub fore: Rgba,
    pub back: Rgba,
    pub pos: V2<f32>,

    sort_key: (i8, i32),
}

impl Sprite {
    pub fn new<A: Into<Rgba>, B: Into<Rgba>>(spr: Spr, pos: V2<f32>, layer: i8, fore: A, back: B) -> Sprite {
        // Fix some numerical inaccuracy noise.
        let pos = pos.map(|x| x.round());
        Sprite {
            spr: spr,
            fore: fore.into(),
            back: back.into(),
            pos: pos,
            sort_key: (layer, -pos.1 as i32)
        }
    }

    #[inline]
    pub fn cmp(&self, other: &Sprite) -> std::cmp::Ordering {
        // Cmp backwards, draw order is from large (far away) to small (close by) values.
        other.sort_key.cmp(&self.sort_key)
    }
}

fn unit_focus_sprites(screen_pos: V2<f32>) -> Vec<Sprite> {
    let mut ret = Vec::new();
    let color = color::LIME;
    for idx in 0..6 {
        ret.push(Sprite::new(Spr::EdgeNW + idx, screen_pos, 0, color, color::BLACK));
    }
    ret
}

fn main() {
    let scroll_speed = 8f32;
    let mut screen_offset = V2(320.0f32, 0.0f32);
    let mut scroll_delta = V2(0.0f32, 0.0f32);
    let mut mouse_cell = V2(-1, -1);

    let mut world = World::new();
    let mut active: HashSet<Entity> = HashSet::new();
    let mut drag_rect = None;

    let mut paused = false;

    let tmx = include_str!("../assets/hexworld.tmx");
    world.load(&tiled::parse(tmx.as_bytes()).unwrap());

    let screen_rect = Rect(V2(-32.0f32, -32.0f32), V2(640.0f32 + 64.0, 360.0f32 + 64.0));
    let mut builder = CanvasBuilder::new()
        .set_size(640, 360)
        .set_frame_interval(0.033f64)
        ;
    Spr::init(&mut builder);
    let mut ctx = builder.build();

    let mut proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
        .view_offset(screen_offset);
    loop {
        match ctx.next_event() {
            Event::RenderFrame => {
                screen_offset = screen_offset - scroll_delta;

                let mut sprites = Vec::new();

                proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
                    .view_offset(screen_offset);

                for pt in proj.inv_project_rectangle(&screen_rect).iter() {
                    let pos = proj.project(pt);
                    Kernel::new(|p| world.terrain_at(p), pt.map(|x| x as i32)).render(
                        |layer, spr, fore, back| {
                            sprites.push(Sprite::new(spr, pos, layer, fore, back));
                        });
                }

                for &e in world.ecs.iter() {
                    if let Some(spr) = cmd::sprite(&world, e, &proj) {
                        // Highlight reticle on focused unit.
                        if active.contains(&e) {
                            for s in unit_focus_sprites(spr.pos).into_iter() {
                                sprites.push(s);
                            }
                        }
                        sprites.push(spr);
                    }
                }

                sprites.sort_by(|a, b| a.cmp(&b));
                for spr in sprites.iter() {
                    ctx.draw_image(spr.spr.get(), spr.pos, 0.5, spr.fore, spr.back);
                }

                if let Some(rect) = drag_rect {
                    ctx.draw_rect(&rect, 0.2, color::CYAN);
                }

                if paused {
                    world.update_standby();
                } else {
                    world.update_active();
                }
            }

            Event::Quit => { return; }

            Event::KeyPress(Key::Escape) => { return; }

            Event::KeyPress(Key::F12) => {
                ctx.save_screenshot(&"azag");
            }

            Event::KeyPress(k) => {
                match k {
                    Key::J => { scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::L => { scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::I => { scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::K => { scroll_delta.1 =  1.0 * scroll_speed; }

                    // XXX: Okay, we need a state object here.
                    Key::W => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::North);
                        }
                    }

                    Key::E => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::NorthEast);
                        }
                    }
                    Key::D => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::SouthEast);
                        }
                    }
                    Key::S => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::South);
                        }
                    }
                    Key::A => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::SouthWest);
                        }
                    }
                    Key::Q => {
                        if !active.is_empty() {
                            paused = true;
                            let rogue = activate_rogue(&mut active);
                            while !cmd::ready_to_act(&world, rogue) { world.update_active(); }
                            cmd::step(&mut world, rogue, Dir6::NorthWest);
                        }
                    }
                    Key::Space => { paused = !paused; }
                    _ => {}
                }
            }

            Event::KeyRelease(k) => {
                match k {
                    Key::J => { scroll_delta.0 = 0.0; }
                    Key::L => { scroll_delta.0 = 0.0; }
                    Key::I => { scroll_delta.1 = 0.0; }
                    Key::K => { scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }

            Event::MouseDrag(MouseButton::Left, p1, p2) => {
                drag_rect = Some(Rect::from_points(p1, p2))
            }

            Event::MouseMove(pos) => {
                mouse_cell = proj.inv_project(pos).map(|x| x.floor() as i32);
            }

            Event::MouseClick(MouseButton::Left) => {
                active.clear();
                if let Some(p) = cmd::mob_at(&world, mouse_cell) {
                    active.insert(p);
                }
            }

            Event::MouseClick(MouseButton::Right) => {
                for &p in active.iter() {
                    if cmd::is_player(&world, p) {
                        cmd::move_to(&mut world, p, mouse_cell);
                    }
                }
            }

            Event::MouseDragEnd(MouseButton::Left, p1, p2) => {
                active.clear();
                drag_rect = None;

                let cell_rect = Rect(V2(-8f32, -8f32), V2(16f32, 8f32));
                let select_rect = Rect::from_points(p1, p2);

                for pt in proj.inv_project_rectangle(&select_rect).iter() {
                    if !select_rect.intersects(&(cell_rect + proj.project(pt))) { continue; }

                    let pos = pt.map(|x| x.floor() as i32);

                    match cmd::mob_at(&world, pos) {
                        Some(e) if cmd::is_player(&world, e) => {
                            active.insert(e);
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }
}

fn activate_rogue(active: &mut HashSet<Entity>) -> Entity {
    let &player = active.iter().next().unwrap();
    active.clear();
    active.insert(player);
    player
}
<commit_msg>Hexworld gamestate<commit_after>/*! Hex map display demo */

extern crate num;
extern crate rustc_serialize;
extern crate image;
extern crate tiled;

#[macro_use] extern crate calx_ecs;
extern crate calx;

mod cmd;
mod render;
mod spr;
mod world;

use std::convert::{Into};
use std::collections::{HashSet};
use calx_ecs::{Entity};
use calx::backend::{CanvasBuilder, Canvas, CanvasUtil, Event, MouseButton, Key};
use calx::{V2, Rect, Rgba, color, Dir6};
use calx::{Projection, Kernel, KernelTerrain};

use spr::Spr;
use render::RenderTerrain;
use world::World;

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Terrain {
    Floor,
    Grass,
    Water,
    Tree,
    Wall,
    Door,
    Window,
    Magma,
    Rock,
    Void,
}

impl Terrain {
    pub fn new(id: u8) -> Terrain {
        // Tiled indexes start from 1.
        let id = id - 1;
        assert!(id <= Terrain::Rock as u8);
        unsafe {
            std::mem::transmute(id)
        }
    }

    pub fn can_walk(self) -> bool {
        use Terrain::*;
        match self {
            Floor | Grass | Door => true,
            _ => false,
        }
    }
}

impl KernelTerrain for Terrain {
    fn is_wall(&self) -> bool {
        use Terrain::*;
        match *self {
            Wall | Door | Window => true,
            _ => false
        }
    }

    fn is_block(&self) -> bool { *self == Terrain::Rock }
}

pub struct Sprite {
    pub spr: Spr,
    pub fore: Rgba,
    pub back: Rgba,
    pub pos: V2<f32>,

    sort_key: (i8, i32),
}

impl Sprite {
    pub fn new<A: Into<Rgba>, B: Into<Rgba>>(spr: Spr, pos: V2<f32>, layer: i8, fore: A, back: B) -> Sprite {
        // Fix some numerical inaccuracy noise.
        let pos = pos.map(|x| x.round());
        Sprite {
            spr: spr,
            fore: fore.into(),
            back: back.into(),
            pos: pos,
            sort_key: (layer, -pos.1 as i32)
        }
    }

    #[inline]
    pub fn cmp(&self, other: &Sprite) -> std::cmp::Ordering {
        // Cmp backwards, draw order is from large (far away) to small (close by) values.
        other.sort_key.cmp(&self.sort_key)
    }
}

fn unit_focus_sprites(screen_pos: V2<f32>) -> Vec<Sprite> {
    let mut ret = Vec::new();
    let color = color::LIME;
    for idx in 0..6 {
        ret.push(Sprite::new(Spr::EdgeNW + idx, screen_pos, 0, color, color::BLACK));
    }
    ret
}

#[derive(Eq, PartialEq)]
pub enum GameMode {
    RealTime,
    Paused,
    Rogue(Entity),
}

pub struct GameState {
    world: World,

    selected: HashSet<Entity>,
    mode: GameMode,

    screen_offset: V2<f32>,
    scroll_delta: V2<f32>,
    pub proj: Projection,
    mouse_cell: V2<i32>,
    drag_rect: Option<Rect<f32>>,
}

impl GameState {
    pub fn new(world: World) -> GameState {
        let screen_offset = V2(320.0f32, 0.0f32);
        GameState {
            world: world,
            selected: HashSet::new(),
            mode: GameMode::RealTime,
            screen_offset: screen_offset,
            scroll_delta: V2(0.0, 0.0),
            proj: Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
                .view_offset(screen_offset),
            mouse_cell: V2(-1, -1),
            drag_rect: None,
        }
    }

    pub fn render(&self, ctx: &mut Canvas) {
        let screen_rect = Rect(V2(-32.0f32, -32.0f32), V2(640.0f32 + 64.0, 360.0f32 + 64.0));

        let mut sprites = Vec::new();

        for pt in self.proj.inv_project_rectangle(&screen_rect).iter() {
            let pos = self.proj.project(pt);
            Kernel::new(|p| self.world.terrain_at(p), pt.map(|x| x as i32)).render(
                |layer, spr, fore, back| {
                    sprites.push(Sprite::new(spr, pos, layer, fore, back));
                });
        }

        for &e in self.world.ecs.iter() {
            if let Some(spr) = cmd::sprite(&self.world, e, &self.proj) {
                // Highlight reticle on focused unit.
                if self.selected.contains(&e) {
                    for s in unit_focus_sprites(spr.pos).into_iter() {
                        sprites.push(s);
                    }
                }
                sprites.push(spr);
            }
        }

        sprites.sort_by(|a, b| a.cmp(&b));
        for spr in sprites.iter() {
            ctx.draw_image(spr.spr.get(), spr.pos, 0.5, spr.fore, spr.back);
        }

        if let Some(rect) = self.drag_rect {
            ctx.draw_rect(&rect, 0.2, color::CYAN);
        }
    }

    pub fn update(&mut self) {
        if self.mode == GameMode::RealTime {
            self.world.update_active();
        } else {
            self.world.update_standby();
        }

        match self.mode {
            GameMode::Rogue(rogue) => {
                let proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap();
                let spr = cmd::sprite(&self.world, rogue, &proj).unwrap();
                self.screen_offset = -spr.pos + V2(320.0, 180.0);
            }
            _ => { self.screen_offset = self.screen_offset - self.scroll_delta; }
        }

        self.proj = Projection::new(V2(16.0, 8.0), V2(-16.0, 8.0)).unwrap()
            .view_offset(self.screen_offset);
    }

    fn go_rogue(&mut self) -> Option<Entity> {
        if let GameMode::Rogue(rogue) = self.mode { return Some(rogue); }

        if let Some(&rogue) = self.selected.iter().next() {
            self.selected.clear();
            self.mode = GameMode::Rogue(rogue);
            Some(rogue)
        } else {
            None
        }
    }

    fn toggle_pause(&mut self) {
        match self.mode {
            GameMode::Rogue(_) | GameMode::Paused => self.mode = GameMode::RealTime,
            GameMode::RealTime => self.mode = GameMode::Paused,
        }
    }

    fn rogue_step(&mut self, dir: Dir6) {
        if let Some(rogue) = self.go_rogue() {
            while !cmd::ready_to_act(&self.world, rogue) { self.world.update_active(); }
            cmd::step(&mut self.world, rogue, dir);
        }
    }

    pub fn process_event(&mut self, evt: Event) {
        let scroll_speed = 8f32;
        match evt {
            Event::KeyPress(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = -1.0 * scroll_speed; }
                    Key::L => { self.scroll_delta.0 =  1.0 * scroll_speed; }
                    Key::I => { self.scroll_delta.1 = -1.0 * scroll_speed; }
                    Key::K => { self.scroll_delta.1 =  1.0 * scroll_speed; }
                    Key::Space => { self.toggle_pause(); }

                    Key::W => { self.rogue_step(Dir6::North); }
                    Key::E => { self.rogue_step(Dir6::NorthEast); }
                    Key::D => { self.rogue_step(Dir6::SouthEast); }
                    Key::S => { self.rogue_step(Dir6::South); }
                    Key::A => { self.rogue_step(Dir6::SouthWest); }
                    Key::Q => { self.rogue_step(Dir6::NorthWest); }
                    _ => {}
                }
            }

            Event::KeyRelease(k) => {
                match k {
                    Key::J => { self.scroll_delta.0 = 0.0; }
                    Key::L => { self.scroll_delta.0 = 0.0; }
                    Key::I => { self.scroll_delta.1 = 0.0; }
                    Key::K => { self.scroll_delta.1 = 0.0; }
                    _ => {}
                }
            }

            Event::MouseDrag(MouseButton::Left, p1, p2) => {
                self.drag_rect = Some(Rect::from_points(p1, p2))
            }

            Event::MouseMove(pos) => {
                self.mouse_cell = self.proj.inv_project(pos).map(|x| x.floor() as i32);
            }

            Event::MouseClick(MouseButton::Left) => {
                self.selected.clear();
                if let Some(p) = cmd::mob_at(&self.world, self.mouse_cell) {
                    self.selected.insert(p);
                }
            }

            Event::MouseClick(MouseButton::Right) => {
                for &p in self.selected.iter() {
                    if cmd::is_player(&self.world, p) {
                        cmd::move_to(&mut self.world, p, self.mouse_cell);
                    }
                }
            }

            Event::MouseDragEnd(MouseButton::Left, p1, p2) => {
                self.selected.clear();
                self.drag_rect = None;

                let cell_rect = Rect(V2(-8f32, -8f32), V2(16f32, 8f32));
                let select_rect = Rect::from_points(p1, p2);

                for pt in self.proj.inv_project_rectangle(&select_rect).iter() {
                    if !select_rect.intersects(&(cell_rect + self.proj.project(pt))) { continue; }

                    let pos = pt.map(|x| x.floor() as i32);

                    match cmd::mob_at(&self.world, pos) {
                        Some(e) if cmd::is_player(&self.world, e) => {
                            self.selected.insert(e);
                        }
                        _ => {}
                    }
                }
            }

            _ => {}
        }
    }
}

fn main() {
    let mut world = World::new();
    let tmx = include_str!("../assets/hexworld.tmx");
    world.load(&tiled::parse(tmx.as_bytes()).unwrap());
    let mut game = GameState::new(world);

    let mut builder = CanvasBuilder::new()
        .set_size(640, 360)
        .set_frame_interval(0.033f64)
        ;
    Spr::init(&mut builder);
    let mut ctx = builder.build();

    loop {
        match ctx.next_event() {
            Event::RenderFrame => {
                game.update();
                game.render(&mut ctx);
            }

            Event::Quit => { return; }

            Event::KeyPress(Key::F12) => {
                ctx.save_screenshot(&"azag");
            }

            Event::KeyPress(Key::Escape) => {
                return;
            }

            e => game.process_event(e),
        }
    }
}
<|endoftext|>


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

mod cli;
mod save_analysis;

use analysis_rust_proto::*;
use anyhow::{Context, Result};
use crypto::{digest::Digest, sha2::Sha256};
use extra_actions_base_rust_proto::*;
use protobuf::Message;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use tempdir::TempDir;
use zip::{write::FileOptions, ZipWriter};

fn main() -> Result<()> {
    let config = cli::parse_arguments();

    // Retrieve the SpawnInfo from the extra action file
    let spawn_info = get_spawn_info(&config.extra_action_path)?;

    // Create temporary directory and run the analysis
    let tmp_dir = TempDir::new("rust_extractor")
        .with_context(|| "Failed to make temporary directory".to_string())?;
    let build_target_arguments: Vec<String> = spawn_info.get_argument().to_vec();
    save_analysis::generate_save_analysis(
        build_target_arguments.clone(),
        PathBuf::new().join(tmp_dir.path()),
    )?;

    // Create the output kzip
    let kzip_file = File::create(&config.output_path)
        .with_context(|| format!("Failed to create kzip file at path {:?}", config.output_path))?;
    let mut kzip = ZipWriter::new(kzip_file);
    kzip.add_directory("root/", FileOptions::default())?;

    // Get KYTHE_CORPUS variable
    let corpus = std::env::var("KYTHE_CORPUS")
        .with_context(|| "KYTHE_CORPUS environment variable is not set".to_string())?;

    // Loop through each source file and insert into kzip
    // Collect input files
    let rust_source_files: Vec<String> = spawn_info
        .get_input_file()
        .to_vec()
        .iter()
        .filter(|file| file.ends_with(".rs"))
        .map(String::from) // Map the &String to a new String
        .collect();
    let mut required_input: Vec<CompilationUnit_FileInput> = Vec::new();
    for file_path in &rust_source_files {
        kzip_add_required_input(file_path, &corpus, &mut kzip, &mut required_input)?;
    }

    // Grab the build target's output path
    let build_output_path: &String = spawn_info
        .get_output_file()
        .get(0)
        .ok_or_else(|| anyhow!("Failed to get output file from spawn info"))?;

    // Add save analysis to kzip
    let save_analysis_path: String = analysis_path_string(&build_output_path, &tmp_dir.path())?;
    kzip_add_required_input(&save_analysis_path, &corpus, &mut kzip, &mut required_input)?;

    // Create the IndexedCompilation and add it to the kzip
    let indexed_compilation = create_indexed_compilation(
        rust_source_files,
        build_target_arguments,
        &build_output_path,
        required_input,
        &corpus,
    );
    let mut indexed_compilation_bytes: Vec<u8> = Vec::new();
    indexed_compilation
        .write_to_vec(&mut indexed_compilation_bytes)
        .with_context(|| "Failed to serialize IndexedCompilation to bytes".to_string())?;
    let indexed_compilation_digest = sha256digest(&indexed_compilation_bytes);
    kzip_add_file(
        format!("root/pbunits/{}", indexed_compilation_digest),
        &indexed_compilation_bytes,
        &mut kzip,
    )?;

    println!("Generated {:?}", &config.output_path);
    Ok(())
}

// Inspired by https://gist.github.com/Igosuki/27e18369fec5a05d4019ac5d321e1779
const SPAWN_INFO_FIELD_NUMBER: u32 = 1003;
const SPAWN_INFO: protobuf::ext::ExtFieldOptional<
    ExtraActionInfo,
    protobuf::types::ProtobufTypeMessage<SpawnInfo>,
> = protobuf::ext::ExtFieldOptional {
    field_number: SPAWN_INFO_FIELD_NUMBER,
    phantom: ::std::marker::PhantomData,
};

/// Attempt to parse ExtraActionInfo protobuf from file_path and extract the
/// SpawnInfo extension
fn get_spawn_info(file_path: impl AsRef<Path>) -> Result<SpawnInfo> {
    let mut file = File::open(file_path).context("Failed to open extra action file")?;

    let mut file_contents_bytes = Vec::new();
    file.read_to_end(&mut file_contents_bytes).context("Failed to read extra action file")?;

    let extra_action = protobuf::parse_from_bytes::<ExtraActionInfo>(&file_contents_bytes)
        .context("Failed to parse extra action protobuf")?;

    SPAWN_INFO.get(&extra_action).ok_or_else(|| anyhow!("SpawnInfo extension missing"))
}

/// Create an IndexedCompilation protobuf from the supplied arguments
///
/// * `source_files` - The names of the source files required by the build
///   target
/// * `arguments` - The arguments used to compile the target
/// * `build_output_path` - The output path of the build target
/// * `required_input` - The generated data for the CompilationUnit
///   `required_input` field
fn create_indexed_compilation(
    source_files: Vec<String>,
    arguments: Vec<String>,
    build_output_path: &str,
    required_input: Vec<CompilationUnit_FileInput>,
    corpus: &str,
) -> IndexedCompilation {
    let mut compilation_unit = CompilationUnit::new();

    let mut vname = VName::new();
    // TODO(Arm1stice): Determine proper Corpus/Root/Path for VName
    vname.set_corpus(corpus.to_string());
    vname.set_language("rust".into());
    compilation_unit.set_v_name(vname);
    compilation_unit.set_source_file(protobuf::RepeatedField::from_vec(source_files));
    compilation_unit.set_argument(protobuf::RepeatedField::from_vec(arguments));
    compilation_unit.set_required_input(protobuf::RepeatedField::from_vec(required_input));
    compilation_unit.set_output_key(build_output_path.to_string());

    let mut indexed_compilation = IndexedCompilation::new();
    indexed_compilation.set_unit(compilation_unit);
    indexed_compilation
}

/// Generate sha256 hex digest of a vector of bytes
fn sha256digest(bytes: &[u8]) -> String {
    let mut sha256 = Sha256::new();
    sha256.input(bytes);
    sha256.result_str()
}

/// Add a file from a path to the kzip and the list of required inputs
///
/// * `file_path_string` - The string path of the file target
/// * `corpus` - The corpus to populate in the VName
/// * `zip_writer` - The ZipWriter to be written to
/// * `required_inputs` - The vector that the new CompilationUnit_FileInput will
///   be added to
fn kzip_add_required_input(
    file_path_string: &str,
    corpus: &str,
    zip_writer: &mut ZipWriter<File>,
    required_inputs: &mut Vec<CompilationUnit_FileInput>,
) -> Result<()> {
    let mut source_file = File::open(file_path_string)
        .with_context(|| format!("Failed open file {:?}", file_path_string))?;
    let mut source_file_contents: Vec<u8> = Vec::new();
    source_file
        .read_to_end(&mut source_file_contents)
        .with_context(|| format!("Failed read file {:?}", file_path_string))?;
    let digest = sha256digest(&source_file_contents);
    kzip_add_file(format!("root/files/{}", digest), &source_file_contents, zip_writer)?;

    // Generate FileInput and add it to the list of required inputs
    let mut file_input = CompilationUnit_FileInput::new();
    let mut file_vname = VName::new();
    file_vname.set_corpus(corpus.to_string());
    file_vname.set_path(file_path_string.to_string());
    file_input.set_v_name(file_vname);

    let mut file_info = FileInfo::new();
    file_info.set_path(file_path_string.to_string());
    file_info.set_digest(digest);
    file_input.set_info(file_info);

    required_inputs.push(file_input);
    Ok(())
}

/// Add a file to the kzip with the specified name and contents
///
/// * `file_name` - The new file's path inside the zip archive
/// * `file_bytes` - The byte contents of the new file
/// * `zip_writer` - The ZipWriter to be written to
fn kzip_add_file(
    file_name: String,
    file_bytes: &[u8],
    zip_writer: &mut ZipWriter<File>,
) -> Result<()> {
    zip_writer
        .start_file(&file_name, FileOptions::default())
        .with_context(|| format!("Failed create file in kzip: {:?}", file_name))?;
    zip_writer
        .write(file_bytes)
        .with_context(|| format!("Failed write file contents to kzip: {:?}", file_name))?;
    Ok(())
}

/// Generate the string path of the save_analysis file using the build target's
/// output path and the temporary base directory
fn analysis_path_string(build_output_path: &str, temp_dir_path: &Path) -> Result<String> {
    // Take the build output path and change the extension to .json
    let analysis_file_name = Path::new(build_output_path).with_extension("json");
    // Extract the file name from the path and convert to a string
    let analysis_file_str = analysis_file_name
        .file_name()
        .unwrap()
        .to_str()
        .ok_or_else(|| anyhow!("Failed to convert path to string"))?;

    // Join the temp_dir_path with "save-analysis/${analysis_file_str}" to get the
    // full path of the save_analysis JSON file
    temp_dir_path
        .join("save-analysis")
        .join(analysis_file_str)
        .to_str()
        .ok_or_else(|| anyhow!("save_analysis file path is not valid UTF-8"))
        .map(|path_str| path_str.to_string())
}
<commit_msg>fix(rust): write_all instead of write in the extractor (#4646)<commit_after>// Copyright 2020 The Kythe Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[macro_use]
extern crate anyhow;

mod cli;
mod save_analysis;

use analysis_rust_proto::*;
use anyhow::{Context, Result};
use crypto::{digest::Digest, sha2::Sha256};
use extra_actions_base_rust_proto::*;
use protobuf::Message;
use std::fs::File;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use tempdir::TempDir;
use zip::{write::FileOptions, ZipWriter};

fn main() -> Result<()> {
    let config = cli::parse_arguments();

    // Retrieve the SpawnInfo from the extra action file
    let spawn_info = get_spawn_info(&config.extra_action_path)?;

    // Create temporary directory and run the analysis
    let tmp_dir = TempDir::new("rust_extractor")
        .with_context(|| "Failed to make temporary directory".to_string())?;
    let build_target_arguments: Vec<String> = spawn_info.get_argument().to_vec();
    save_analysis::generate_save_analysis(
        build_target_arguments.clone(),
        PathBuf::new().join(tmp_dir.path()),
    )?;

    // Create the output kzip
    let kzip_file = File::create(&config.output_path)
        .with_context(|| format!("Failed to create kzip file at path {:?}", config.output_path))?;
    let mut kzip = ZipWriter::new(kzip_file);
    kzip.add_directory("root/", FileOptions::default())?;

    // Get KYTHE_CORPUS variable
    let corpus = std::env::var("KYTHE_CORPUS")
        .with_context(|| "KYTHE_CORPUS environment variable is not set".to_string())?;

    // Loop through each source file and insert into kzip
    // Collect input files
    let rust_source_files: Vec<String> = spawn_info
        .get_input_file()
        .to_vec()
        .iter()
        .filter(|file| file.ends_with(".rs"))
        .map(String::from) // Map the &String to a new String
        .collect();
    let mut required_input: Vec<CompilationUnit_FileInput> = Vec::new();
    for file_path in &rust_source_files {
        kzip_add_required_input(file_path, &corpus, &mut kzip, &mut required_input)?;
    }

    // Grab the build target's output path
    let build_output_path: &String = spawn_info
        .get_output_file()
        .get(0)
        .ok_or_else(|| anyhow!("Failed to get output file from spawn info"))?;

    // Add save analysis to kzip
    let save_analysis_path: String = analysis_path_string(&build_output_path, &tmp_dir.path())?;
    kzip_add_required_input(&save_analysis_path, &corpus, &mut kzip, &mut required_input)?;

    // Create the IndexedCompilation and add it to the kzip
    let indexed_compilation = create_indexed_compilation(
        rust_source_files,
        build_target_arguments,
        &build_output_path,
        required_input,
        &corpus,
    );
    let mut indexed_compilation_bytes: Vec<u8> = Vec::new();
    indexed_compilation
        .write_to_vec(&mut indexed_compilation_bytes)
        .with_context(|| "Failed to serialize IndexedCompilation to bytes".to_string())?;
    let indexed_compilation_digest = sha256digest(&indexed_compilation_bytes);
    kzip_add_file(
        format!("root/pbunits/{}", indexed_compilation_digest),
        &indexed_compilation_bytes,
        &mut kzip,
    )?;

    println!("Generated {:?}", &config.output_path);
    Ok(())
}

// Inspired by https://gist.github.com/Igosuki/27e18369fec5a05d4019ac5d321e1779
const SPAWN_INFO_FIELD_NUMBER: u32 = 1003;
const SPAWN_INFO: protobuf::ext::ExtFieldOptional<
    ExtraActionInfo,
    protobuf::types::ProtobufTypeMessage<SpawnInfo>,
> = protobuf::ext::ExtFieldOptional {
    field_number: SPAWN_INFO_FIELD_NUMBER,
    phantom: ::std::marker::PhantomData,
};

/// Attempt to parse ExtraActionInfo protobuf from file_path and extract the
/// SpawnInfo extension
fn get_spawn_info(file_path: impl AsRef<Path>) -> Result<SpawnInfo> {
    let mut file = File::open(file_path).context("Failed to open extra action file")?;

    let mut file_contents_bytes = Vec::new();
    file.read_to_end(&mut file_contents_bytes).context("Failed to read extra action file")?;

    let extra_action = protobuf::parse_from_bytes::<ExtraActionInfo>(&file_contents_bytes)
        .context("Failed to parse extra action protobuf")?;

    SPAWN_INFO.get(&extra_action).ok_or_else(|| anyhow!("SpawnInfo extension missing"))
}

/// Create an IndexedCompilation protobuf from the supplied arguments
///
/// * `source_files` - The names of the source files required by the build
///   target
/// * `arguments` - The arguments used to compile the target
/// * `build_output_path` - The output path of the build target
/// * `required_input` - The generated data for the CompilationUnit
///   `required_input` field
fn create_indexed_compilation(
    source_files: Vec<String>,
    arguments: Vec<String>,
    build_output_path: &str,
    required_input: Vec<CompilationUnit_FileInput>,
    corpus: &str,
) -> IndexedCompilation {
    let mut compilation_unit = CompilationUnit::new();

    let mut vname = VName::new();
    // TODO(Arm1stice): Determine proper Corpus/Root/Path for VName
    vname.set_corpus(corpus.to_string());
    vname.set_language("rust".into());
    compilation_unit.set_v_name(vname);
    compilation_unit.set_source_file(protobuf::RepeatedField::from_vec(source_files));
    compilation_unit.set_argument(protobuf::RepeatedField::from_vec(arguments));
    compilation_unit.set_required_input(protobuf::RepeatedField::from_vec(required_input));
    compilation_unit.set_output_key(build_output_path.to_string());

    let mut indexed_compilation = IndexedCompilation::new();
    indexed_compilation.set_unit(compilation_unit);
    indexed_compilation
}

/// Generate sha256 hex digest of a vector of bytes
fn sha256digest(bytes: &[u8]) -> String {
    let mut sha256 = Sha256::new();
    sha256.input(bytes);
    sha256.result_str()
}

/// Add a file from a path to the kzip and the list of required inputs
///
/// * `file_path_string` - The string path of the file target
/// * `corpus` - The corpus to populate in the VName
/// * `zip_writer` - The ZipWriter to be written to
/// * `required_inputs` - The vector that the new CompilationUnit_FileInput will
///   be added to
fn kzip_add_required_input(
    file_path_string: &str,
    corpus: &str,
    zip_writer: &mut ZipWriter<File>,
    required_inputs: &mut Vec<CompilationUnit_FileInput>,
) -> Result<()> {
    let mut source_file = File::open(file_path_string)
        .with_context(|| format!("Failed open file {:?}", file_path_string))?;
    let mut source_file_contents: Vec<u8> = Vec::new();
    source_file
        .read_to_end(&mut source_file_contents)
        .with_context(|| format!("Failed read file {:?}", file_path_string))?;
    let digest = sha256digest(&source_file_contents);
    kzip_add_file(format!("root/files/{}", digest), &source_file_contents, zip_writer)?;

    // Generate FileInput and add it to the list of required inputs
    let mut file_input = CompilationUnit_FileInput::new();
    let mut file_vname = VName::new();
    file_vname.set_corpus(corpus.to_string());
    file_vname.set_path(file_path_string.to_string());
    file_input.set_v_name(file_vname);

    let mut file_info = FileInfo::new();
    file_info.set_path(file_path_string.to_string());
    file_info.set_digest(digest);
    file_input.set_info(file_info);

    required_inputs.push(file_input);
    Ok(())
}

/// Add a file to the kzip with the specified name and contents
///
/// * `file_name` - The new file's path inside the zip archive
/// * `file_bytes` - The byte contents of the new file
/// * `zip_writer` - The ZipWriter to be written to
fn kzip_add_file(
    file_name: String,
    file_bytes: &[u8],
    zip_writer: &mut ZipWriter<File>,
) -> Result<()> {
    zip_writer
        .start_file(&file_name, FileOptions::default())
        .with_context(|| format!("Failed create file in kzip: {:?}", file_name))?;
    zip_writer
        .write_all(file_bytes)
        .with_context(|| format!("Failed write file contents to kzip: {:?}", file_name))?;
    Ok(())
}

/// Generate the string path of the save_analysis file using the build target's
/// output path and the temporary base directory
fn analysis_path_string(build_output_path: &str, temp_dir_path: &Path) -> Result<String> {
    // Take the build output path and change the extension to .json
    let analysis_file_name = Path::new(build_output_path).with_extension("json");
    // Extract the file name from the path and convert to a string
    let analysis_file_str = analysis_file_name
        .file_name()
        .unwrap()
        .to_str()
        .ok_or_else(|| anyhow!("Failed to convert path to string"))?;

    // Join the temp_dir_path with "save-analysis/${analysis_file_str}" to get the
    // full path of the save_analysis JSON file
    temp_dir_path
        .join("save-analysis")
        .join(analysis_file_str)
        .to_str()
        .ok_or_else(|| anyhow!("save_analysis file path is not valid UTF-8"))
        .map(|path_str| path_str.to_string())
}
<|endoftext|>


<commit_before>/*!
An adapter for ImageCacheTask that does local caching to avoid
extra message traffic, it also avoids waiting on the same image
multiple times and thus triggering reflows multiple times.
*/

use std::net::url::Url;
use pipes::{Port, Chan, stream};
use image_cache_task::{ImageCacheTask, ImageResponseMsg, Prefetch, Decode, GetImage, WaitForImage};

pub fn LocalImageCache(
    image_cache_task: ImageCacheTask
) -> LocalImageCache {
    LocalImageCache {
        image_cache_task: image_cache_task,
        round_number: 0,
        mut on_image_available: None
    }
}

pub struct LocalImageCache {
    priv image_cache_task: ImageCacheTask,
    priv mut round_number: uint,
    priv mut on_image_available: Option<~fn(ImageResponseMsg)>
}

pub impl LocalImageCache {
    /// The local cache will only do a single remote request for a given
    /// URL in each 'round'. Layout should call this each time it begins
    fn next_round(on_image_available: ~fn(ImageResponseMsg)) {
        self.round_number += 1;
        self.on_image_available = Some(move on_image_available);
    }

    fn prefetch(url: &Url) {
        self.image_cache_task.send(Prefetch(copy *url));
    }

    fn decode(url: &Url) {
        self.image_cache_task.send(Decode(copy *url));
    }

    // FIXME: Should return a Future
    fn get_image(url: &Url) -> Port<ImageResponseMsg> {
        let (response_chan, response_port) = pipes::stream();
        self.image_cache_task.send(image_cache_task::GetImage(copy *url, response_chan));

        let response = response_port.recv();
        match response {
            image_cache_task::ImageNotReady => {
                // Need to reflow when the image is available
                // FIXME: Instead we should be just passing a Future
                // to the caller, then to the display list. Finally,
                // the compositor should be resonsible for waiting
                // on the image to load and triggering layout
                let image_cache_task = self.image_cache_task.clone();
                assert self.on_image_available.is_some();
                let on_image_available = {copy *self.on_image_available.get_ref()};
                let url = copy *url;
                do task::spawn |move url, move on_image_available| {
                    let (response_chan, response_port) = pipes::stream();
                    image_cache_task.send(image_cache_task::WaitForImage(copy url, response_chan));
                    on_image_available(response_port.recv());
                }
            }
            _ => ()
        }

        let (chan, port) = pipes::stream();
        chan.send(response);
        return port;
    }
}

<commit_msg>Actually do caching in LocalImageCache<commit_after>/*!
An adapter for ImageCacheTask that does local caching to avoid
extra message traffic, it also avoids waiting on the same image
multiple times and thus triggering reflows multiple times.
*/

use clone_arc = std::arc::clone;
use std::net::url::Url;
use pipes::{Port, Chan, stream};
use image_cache_task::{ImageCacheTask, ImageResponseMsg, Prefetch, Decode, GetImage, WaitForImage, ImageReady, ImageNotReady, ImageFailed};
use util::url::{UrlMap, url_map};

pub fn LocalImageCache(
    image_cache_task: ImageCacheTask
) -> LocalImageCache {
    LocalImageCache {
        image_cache_task: image_cache_task,
        round_number: 1,
        mut on_image_available: None,
        state_map: url_map()
    }
}

pub struct LocalImageCache {
    priv image_cache_task: ImageCacheTask,
    priv mut round_number: uint,
    priv mut on_image_available: Option<~fn(ImageResponseMsg)>,
    priv state_map: UrlMap<@ImageState>
}

priv struct ImageState {
    mut prefetched: bool,
    mut decoded: bool,
    mut last_request_round: uint,
    mut last_response: ImageResponseMsg
}

#[allow(non_implicitly_copyable_typarams)] // Using maps of Urls
pub impl LocalImageCache {
    /// The local cache will only do a single remote request for a given
    /// URL in each 'round'. Layout should call this each time it begins
    // FIXME: 'pub' is an unexpected token?
    /* pub */ fn next_round(on_image_available: ~fn(ImageResponseMsg)) {
        self.round_number += 1;
        self.on_image_available = Some(move on_image_available);
    }

    pub fn prefetch(url: &Url) {
        let state = self.get_state(url);
        if !state.prefetched {
            self.image_cache_task.send(Prefetch(copy *url));
            state.prefetched = true;
        }
    }

    pub fn decode(url: &Url) {
        let state = self.get_state(url);
        if !state.decoded {
            self.image_cache_task.send(Decode(copy *url));
            state.decoded = true;
        }
    }

    // FIXME: Should return a Future
    pub fn get_image(url: &Url) -> Port<ImageResponseMsg> {
        let state = self.get_state(url);

        // Save the previous round number for comparison
        let last_round = state.last_request_round;
        // Set the current round number for this image
        state.last_request_round = self.round_number;

        match state.last_response {
            ImageReady(ref image) => {
                // FIXME: appease borrowck
                unsafe {
                    let (chan, port) = pipes::stream();
                    chan.send(ImageReady(clone_arc(image)));
                    return port;
                }
            }
            ImageNotReady => {
                if last_round == self.round_number {
                    let (chan, port) = pipes::stream();
                    chan.send(ImageNotReady);
                    return port;
                } else {
                    // We haven't requested the image from the
                    // remote cache this round
                }
            }
            ImageFailed => {
                let (chan, port) = pipes::stream();
                chan.send(ImageFailed);
                return port;
            }
        }

        let (response_chan, response_port) = pipes::stream();
        self.image_cache_task.send(GetImage(copy *url, response_chan));

        let response = response_port.recv();
        match response {
            ImageNotReady => {
                // Need to reflow when the image is available
                // FIXME: Instead we should be just passing a Future
                // to the caller, then to the display list. Finally,
                // the compositor should be resonsible for waiting
                // on the image to load and triggering layout
                let image_cache_task = self.image_cache_task.clone();
                assert self.on_image_available.is_some();
                let on_image_available = {copy *self.on_image_available.get_ref()};
                let url = copy *url;
                do task::spawn |move url, move on_image_available| {
                    let (response_chan, response_port) = pipes::stream();
                    image_cache_task.send(WaitForImage(copy url, response_chan));
                    on_image_available(response_port.recv());
                }
            }
            _ => ()
        }

        // Put a copy of the response in the cache
        let response_copy = match response {
            ImageReady(ref image) => ImageReady(clone_arc(image)),
            ImageNotReady => ImageNotReady,
            ImageFailed => ImageFailed
        };
        state.last_response = move response_copy;

        let (chan, port) = pipes::stream();
        chan.send(response);
        return port;
    }

    priv fn get_state(url: &Url) -> @ImageState {
        match self.state_map.find(copy *url) {
            Some(state) => state,
            None => {
                let new_state = @ImageState {
                    prefetched: false,
                    decoded: false,
                    last_request_round: 0,
                    last_response: ImageNotReady
                };
                self.state_map.insert(copy *url, new_state);
                self.get_state(url)
            }
        }
    }
}

<|endoftext|>


<commit_before>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate shader_version;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate sdl2;
extern crate sdl2_window;

use std::cell::RefCell;
use std::mem::transmute;
use piston::quack::Set;
use piston::window::{ WindowSettings, CaptureCursor };
use piston::event::RenderEvent;
use shader_version::OpenGL;
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::Resources;
use gfx::traits::{
    Device,
    DeviceExt,
    Factory,
    FactoryExt,
    ToSlice
};
use sdl2::video::gl_get_proc_address;
use sdl2_window::Sdl2Window;

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

const VERTEX_SRC: [&'static [u8]; 2] = [ b"
    #version 120
    attribute vec3 a_pos;
    attribute vec2 a_tex_coord;
    varying vec2 v_TexCoord;
    uniform mat4 u_model_view_proj;
    void main() {
        v_TexCoord = a_tex_coord;
        gl_Position = u_model_view_proj * vec4(a_pos, 1.0);
    }
", b"
    #version 150 core
    in vec3 a_pos;
    in vec2 a_tex_coord;
    out vec2 v_TexCoord;
    uniform mat4 u_model_view_proj;
    void main() {
        v_TexCoord = a_tex_coord;
        gl_Position = u_model_view_proj * vec4(a_pos, 1.0);
    }
"];

const FRAGMENT_SRC: [&'static [u8]; 2] = [ b"
    #version 120
    varying vec2 v_TexCoord;
    uniform sampler2D t_color;
    void main() {
        vec4 tex = texture2D(t_color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        gl_FragColor = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
", b"
    #version 150 core
    in vec2 v_TexCoord;
    out vec4 o_Color;
    uniform sampler2D t_color;
    void main() {
        vec4 tex = texture(t_color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        o_Color = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"];

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let mut window = Sdl2Window::new(
        OpenGL::_3_2,
        WindowSettings {
            title: "cube".to_string(),
            size: [win_width, win_height],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    window.set_mut(CaptureCursor(true));

    let vertex = gfx::ShaderSource {
        glsl_120: Some(VERTEX_SRC[0]),
        glsl_150: Some(VERTEX_SRC[1]),
        .. gfx::ShaderSource::empty()
    };
    let fragment = gfx::ShaderSource {
        glsl_120: Some(FRAGMENT_SRC[0]),
        glsl_150: Some(FRAGMENT_SRC[1]),
        .. gfx::ShaderSource::empty()
    };

    let mut device = gfx_device_gl::GlDevice::new(|s| unsafe {
        transmute(gl_get_proc_address(s))
    });
    let frame = gfx::Frame::new(win_width as u16, win_height as u16);
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = device.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = device.create_buffer_static(index_data)
                      .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1,
        height: 1,
        depth: 1,
        levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = device.create_texture(tinfo).unwrap();
    device.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00]
    ).unwrap();

    let sampler = device.create_sampler(
        gfx::tex::SamplerInfo::new(
            gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp
        )
    );

    let shader_model = device.get_capabilities().shader_model;

    let program = device.link_program(
        vertex.choose(shader_model).unwrap(),
        fragment.choose(shader_model).unwrap()
    ).unwrap();

    let mut graphics = device.into_graphics();

    let data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let mut batch = graphics.make_batch(&program, data, &mesh, slice, &state).unwrap();

    let model = vecmath::mat4_id();
    let projection = CameraPerspective {
        fov: 90.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: (win_width as f32) / (win_height as f32)
    }.projection();
    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    let window = RefCell::new(window);
    for e in piston::events(&window) {
        first_person.event(&e);
        if let Some(args) = e.render_args() {
            graphics.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &frame
            );
            batch.params.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            graphics.draw(&batch, &frame).unwrap();
            graphics.end_frame();
        }
    }
}
<commit_msg>Update gfx_cube example to latest gfx<commit_after>#![feature(plugin, custom_attribute)]
#![plugin(gfx_macros)]

extern crate piston;
extern crate shader_version;
extern crate vecmath;
extern crate camera_controllers;
extern crate gfx;
extern crate gfx_device_gl;
extern crate sdl2;
extern crate sdl2_window;

use std::cell::RefCell;
use std::mem::transmute;
use piston::quack::Set;
use piston::window::{ WindowSettings, CaptureCursor };
use piston::event::RenderEvent;
use shader_version::OpenGL;
use camera_controllers::{
    FirstPersonSettings,
    FirstPerson,
    CameraPerspective,
    model_view_projection
};
use gfx::Resources;
use gfx::traits::*;
use sdl2::video::gl_get_proc_address;
use sdl2_window::Sdl2Window;

//----------------------------------------
// Cube associated data

#[vertex_format]
#[derive(Copy)]
struct Vertex {
    #[as_float]
    a_pos: [i8; 3],
    #[as_float]
    a_tex_coord: [u8; 2],
}

impl Vertex {
    fn new(pos: [i8; 3], tc: [u8; 2]) -> Vertex {
        Vertex {
            a_pos: pos,
            a_tex_coord: tc,
        }
    }
}

#[shader_param]
struct Params<R: Resources> {
    u_model_view_proj: [[f32; 4]; 4],
    t_color: gfx::shade::TextureParam<R>,
}

const VERTEX_SRC: [&'static [u8]; 2] = [ b"
    #version 120
    attribute vec3 a_pos;
    attribute vec2 a_tex_coord;
    varying vec2 v_TexCoord;
    uniform mat4 u_model_view_proj;
    void main() {
        v_TexCoord = a_tex_coord;
        gl_Position = u_model_view_proj * vec4(a_pos, 1.0);
    }
", b"
    #version 150 core
    in vec3 a_pos;
    in vec2 a_tex_coord;
    out vec2 v_TexCoord;
    uniform mat4 u_model_view_proj;
    void main() {
        v_TexCoord = a_tex_coord;
        gl_Position = u_model_view_proj * vec4(a_pos, 1.0);
    }
"];

const FRAGMENT_SRC: [&'static [u8]; 2] = [ b"
    #version 120
    varying vec2 v_TexCoord;
    uniform sampler2D t_color;
    void main() {
        vec4 tex = texture2D(t_color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        gl_FragColor = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
", b"
    #version 150 core
    in vec2 v_TexCoord;
    out vec4 o_Color;
    uniform sampler2D t_color;
    void main() {
        vec4 tex = texture(t_color, v_TexCoord);
        float blend = dot(v_TexCoord-vec2(0.5,0.5), v_TexCoord-vec2(0.5,0.5));
        o_Color = mix(tex, vec4(0.0,0.0,0.0,0.0), blend*1.0);
    }
"];

//----------------------------------------

fn main() {
    let (win_width, win_height) = (640, 480);
    let mut window = Sdl2Window::new(
        OpenGL::_3_2,
        WindowSettings {
            title: "cube".to_string(),
            size: [win_width, win_height],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    window.set_mut(CaptureCursor(true));

    let vertex = gfx::ShaderSource {
        glsl_120: Some(VERTEX_SRC[0]),
        glsl_150: Some(VERTEX_SRC[1]),
        .. gfx::ShaderSource::empty()
    };
    let fragment = gfx::ShaderSource {
        glsl_120: Some(FRAGMENT_SRC[0]),
        glsl_150: Some(FRAGMENT_SRC[1]),
        .. gfx::ShaderSource::empty()
    };

    let mut device = gfx_device_gl::GlDevice::new(|s| unsafe {
        transmute(gl_get_proc_address(s))
    });
    let frame = gfx::Frame::new(win_width as u16, win_height as u16);
    let state = gfx::DrawState::new().depth(gfx::state::Comparison::LessEqual, true);

    let vertex_data = vec![
        //top (0, 0, 1)
        Vertex::new([-1, -1,  1], [0, 0]),
        Vertex::new([ 1, -1,  1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //bottom (0, 0, -1)
        Vertex::new([ 1,  1, -1], [0, 0]),
        Vertex::new([-1,  1, -1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
        //right (1, 0, 0)
        Vertex::new([ 1, -1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([ 1, -1,  1], [0, 1]),
        //left (-1, 0, 0)
        Vertex::new([-1,  1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([-1,  1, -1], [0, 1]),
        //front (0, 1, 0)
        Vertex::new([-1,  1, -1], [0, 0]),
        Vertex::new([ 1,  1, -1], [1, 0]),
        Vertex::new([ 1,  1,  1], [1, 1]),
        Vertex::new([-1,  1,  1], [0, 1]),
        //back (0, -1, 0)
        Vertex::new([ 1, -1,  1], [0, 0]),
        Vertex::new([-1, -1,  1], [1, 0]),
        Vertex::new([-1, -1, -1], [1, 1]),
        Vertex::new([ 1, -1, -1], [0, 1]),
    ];

    let mesh = device.create_mesh(&vertex_data);

    let index_data: &[u8] = &[
         0,  1,  2,  2,  3,  0, // top
         4,  6,  5,  6,  4,  7, // bottom
         8,  9, 10, 10, 11,  8, // right
        12, 14, 13, 14, 12, 16, // left
        16, 18, 17, 18, 16, 19, // front
        20, 21, 22, 22, 23, 20, // back
    ];

    let slice = device.create_buffer_index(index_data)
                      .to_slice(gfx::PrimitiveType::TriangleList);

    let tinfo = gfx::tex::TextureInfo {
        width: 1,
        height: 1,
        depth: 1,
        levels: 1,
        kind: gfx::tex::TextureKind::Texture2D,
        format: gfx::tex::RGBA8,
    };
    let img_info = tinfo.to_image_info();
    let texture = device.create_texture(tinfo).unwrap();
    device.update_texture(
        &texture,
        &img_info,
        &[0x20u8, 0xA0, 0xC0, 0x00]
    ).unwrap();

    let sampler = device.create_sampler(
        gfx::tex::SamplerInfo::new(
            gfx::tex::FilterMethod::Bilinear,
            gfx::tex::WrapMode::Clamp
        )
    );

    let shader_model = device.get_capabilities().shader_model;

    let program = device.link_program(
        vertex.choose(shader_model).unwrap(),
        fragment.choose(shader_model).unwrap()
    ).unwrap();

    let mut graphics = device.into_graphics();

    let data = Params {
        u_model_view_proj: vecmath::mat4_id(),
        t_color: (texture, Some(sampler)),
    };

    let mut batch = graphics.make_batch(&program, data, &mesh, slice, &state).unwrap();

    let model = vecmath::mat4_id();
    let projection = CameraPerspective {
        fov: 90.0,
        near_clip: 0.1,
        far_clip: 1000.0,
        aspect_ratio: (win_width as f32) / (win_height as f32)
    }.projection();
    let mut first_person = FirstPerson::new(
        [0.5, 0.5, 4.0],
        FirstPersonSettings::keyboard_wasd()
    );

    let window = RefCell::new(window);
    for e in piston::events(&window) {
        first_person.event(&e);
        if let Some(args) = e.render_args() {
            graphics.clear(
                gfx::ClearData {
                    color: [0.3, 0.3, 0.3, 1.0],
                    depth: 1.0,
                    stencil: 0,
                },
                gfx::COLOR | gfx::DEPTH,
                &frame
            );
            batch.params.u_model_view_proj = model_view_projection(
                model,
                first_person.camera(args.ext_dt).orthogonal(),
                projection
            );
            graphics.draw(&batch, &frame).unwrap();
            graphics.end_frame();
        }
    }
}
<|endoftext|>


<commit_before>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let gutter_color = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let diagnostic_color = ColorSpec::new()
        .set_fg(Some(severity_color(diagnostic.severity)))
        .clone();

    let highlight_color = ColorSpec::new().set_bold(true).set_intense(true).clone();

    // Diagnostic header

    writer.set_color(
        &highlight_color
            .clone()
            .set_fg(Some(severity_color(diagnostic.severity))),
    )?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&highlight_color)?;
    writeln!(writer, ": {}", diagnostic.message)?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    writeln!(writer, "- {}", label.message)?
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                writeln!(
                    writer,
                    "{: >width$} - {file}:{line}:{column}",
                    "",
                    width = gutter_padding,
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   │
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                let label_color = match label.style {
                    LabelStyle::Primary => diagnostic_color.clone(),
                    LabelStyle::Secondary => {
                        ColorSpec::new().set_fg(Some(secondary_color())).clone()
                    },
                };

                // Write prefix to marked section

                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                writer.reset()?;
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_color)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_color)?;
                        writeln!(writer, "{}", line)?;
                    }

                    writer.set_color(&gutter_color)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_color)?;
                    write!(writer, "{}", line)?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                writer.reset()?;
                writeln!(writer, "{}", suffix)?;

                // Write mark and label

                writer.set_color(&gutter_color)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_color)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                writer.reset()?;

                if !label.message.is_empty() {
                    writer.set_color(&label_color)?;
                    writeln!(writer, " {}", label.message)?;
                    writer.reset()?;
                }
                writer.set_color(&gutter_color)?;
                writeln!(writer, "{: >width$} │", "", width = gutter_padding)?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<commit_msg>Clean up color handling<commit_after>use codespan::{CodeMap, LineIndex, RawIndex};
use std::io;
use termcolor::{Color, ColorSpec, WriteColor};

use crate::{Diagnostic, LabelStyle, Severity};

pub fn emit<W, S>(mut writer: W, codemap: &CodeMap<S>, diagnostic: &Diagnostic) -> io::Result<()>
where
    W: WriteColor,
    S: AsRef<str>,
{
    let severity_color = severity_color(diagnostic.severity);
    let gutter_spec = ColorSpec::new().set_fg(Some(gutter_color())).clone();
    let primary_spec = ColorSpec::new().set_fg(Some(severity_color)).clone();
    let secondary_spec = ColorSpec::new().set_fg(Some(secondary_color())).clone();
    let header_message_spec = ColorSpec::new().set_bold(true).set_intense(true).clone();
    let header_primary_spec = header_message_spec.clone().set_fg(Some(severity_color)).clone();

    // Diagnostic header

    writer.set_color(&header_primary_spec)?;

    // Severity
    //
    // ```
    // error
    // ```
    write!(writer, "{}", severity_name(diagnostic.severity))?;

    // Error code
    //
    // ```
    // [E0001]
    // ```
    if let Some(code) = &diagnostic.code {
        write!(writer, "[{}]", code)?;
    }

    // Diagnostic message
    //
    // ```
    // : Unexpected type in `+` application
    // ```
    writer.set_color(&header_message_spec)?;
    writeln!(writer, ": {}", diagnostic.message)?;
    writer.reset()?;

    // Diagnostic Labels

    for label in &diagnostic.labels {
        match codemap.find_file(label.span.start()) {
            None => {
                if !label.message.is_empty() {
                    writeln!(writer, "- {}", label.message)?
                }
            },
            Some(file) => {
                let (start_line, column) =
                    file.location(label.span.start()).expect("location_start");
                let (end_line, _) = file.location(label.span.end()).expect("location_end");
                // Use the length of the last line number as the gutter padding
                let gutter_padding = format!("{}", end_line.number()).len();

                // File name
                //
                // ```
                // - <test>:2:9
                // ```

                writeln!(
                    writer,
                    "{: >width$} - {file}:{line}:{column}",
                    "",
                    width = gutter_padding,
                    file = file.name(),
                    line = start_line.number(),
                    column = column.number(),
                )?;

                // Source code snippet
                //
                // ```
                //   │
                // 2 │ (+ test "")
                //   │         ^^ Expected integer but got string
                //   │
                // ```

                let start_line_span = file.line_span(start_line).expect("line_span");
                let end_line_span = file.line_span(end_line).expect("line_span");

                let label_spec = match label.style {
                    LabelStyle::Primary => &primary_spec,
                    LabelStyle::Secondary => &secondary_spec,
                };

                // Write prefix to marked section

                writer.set_color(&gutter_spec)?;
                writeln!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                write!(
                    writer,
                    "{: >width$} │ ",
                    start_line.number(),
                    width = gutter_padding,
                )?;

                let prefix = file
                    .src_slice(start_line_span.with_end(label.span.start()))
                    .expect("prefix");
                writer.reset()?;
                write!(writer, "{}", prefix)?;

                // Write marked section
                let mark_len = if start_line == end_line {
                    // Single line

                    let marked = file.src_slice(label.span).expect("marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;
                    marked.len()
                } else {
                    // Multiple lines

                    let marked = file
                        .src_slice(start_line_span.with_start(label.span.start()))
                        .expect("start_of_marked");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", marked)?;

                    for line_index in ((start_line.to_usize() + 1)..end_line.to_usize())
                        .map(|i| LineIndex::from(i as RawIndex))
                    {
                        writer.set_color(&gutter_spec)?;
                        write!(
                            writer,
                            "{: >width$} │ ",
                            line_index.number(),
                            width = gutter_padding,
                        )?;

                        let line_span = file.line_span(line_index).expect("marked_line_span");
                        let line = file
                            .src_slice(line_span)
                            .expect("line")
                            .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                        writer.set_color(&label_spec)?;
                        writeln!(writer, "{}", line)?;
                    }

                    writer.set_color(&gutter_spec)?;
                    write!(
                        writer,
                        "{: >width$} │ ",
                        end_line.number(),
                        width = gutter_padding,
                    )?;
                    let line = file
                        .src_slice(end_line_span.with_end(label.span.end()))
                        .expect("line");
                    writer.set_color(&label_spec)?;
                    write!(writer, "{}", line)?;
                    line.len()
                };

                // Write suffix to marked section

                let suffix = file
                    .src_slice(end_line_span.with_start(label.span.end()))
                    .expect("suffix")
                    .trim_end_matches(|ch: char| ch == '\r' || ch == '\n');
                writer.reset()?;
                writeln!(writer, "{}", suffix)?;

                // Write mark and label

                writer.set_color(&gutter_spec)?;
                write!(writer, "{: >width$} │ ", "", width = gutter_padding)?;
                writer.reset()?;

                writer.set_color(&label_spec)?;
                write!(writer, "{: >width$}", "", width = prefix.len())?;
                for _ in 0..mark_len {
                    write!(writer, "{}", underline_mark(label.style))?;
                }
                writer.reset()?;

                if !label.message.is_empty() {
                    writer.set_color(&label_spec)?;
                    writeln!(writer, " {}", label.message)?;
                    writer.reset()?;
                }
                writer.set_color(&gutter_spec)?;
                writeln!(writer, "{: >width$} │", "", width = gutter_padding)?;
                writer.reset()?;
            },
        }
    }
    Ok(())
}

// Blue is really difficult to see on the standard windows command line
// FIXME: Make colors configurable
#[cfg(windows)]
const BLUE: Color = Color::Cyan;
#[cfg(not(windows))]
const BLUE: Color = Color::Blue;

/// Return the termcolor to use when rendering messages of this diagnostic severity.
fn severity_color(severity: Severity) -> Color {
    match severity {
        Severity::Bug | Severity::Error => Color::Red,
        Severity::Warning => Color::Yellow,
        Severity::Note => Color::Green,
        Severity::Help => Color::Cyan,
    }
}

/// The color to use for secondary highlights.
fn secondary_color() -> Color {
    BLUE
}

/// The color to use for gutters highlights.
fn gutter_color() -> Color {
    BLUE
}

/// A string that explains this diagnostic severity.
fn severity_name(severity: Severity) -> &'static str {
    match severity {
        Severity::Bug => "bug",
        Severity::Error => "error",
        Severity::Warning => "warning",
        Severity::Note => "note",
        Severity::Help => "help",
    }
}

/// The mark used for the underlined section of code.
fn underline_mark(label_style: LabelStyle) -> char {
    match label_style {
        LabelStyle::Primary => '^',
        LabelStyle::Secondary => '-',
    }
}
<|endoftext|>