<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(core_intrinsics)]
#![allow(stable_features)] // lying about question_mark because 1.14.0 isn't released!

#![feature(proc_macro)]

#[macro_use]
extern crate serde_derive;

extern crate cgmath;
extern crate copypasta;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate mio;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;
extern crate vte;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod event;
mod event_loop;
mod index;
mod input;
mod meter;
mod renderer;
mod sync;
mod term;
mod tty;
mod util;
pub mod ansi;
pub mod config;
pub mod grid;

use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering};

use parking_lot::{MutexGuard};

use event_loop::EventLoop;

use config::Config;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use sync::FairMutex;
use term::Term;
use tty::process_should_exit;

/// Channel used by resize handling on mac
static mut RESIZE_SENDER: Option<mpsc::Sender<(u32, u32)>> = None;

#[derive(Clone)]
pub struct Flag(Arc<AtomicBool>);
impl Flag {
    pub fn new(initial_value: bool) -> Flag {
        Flag(Arc::new(AtomicBool::new(initial_value)))
    }

    #[inline]
    pub fn get(&self) -> bool {
        self.0.load(Ordering::Acquire)
    }

    #[inline]
    pub fn set(&self, value: bool) {
        self.0.store(value, Ordering::Release)
    }
}

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = RESIZE_SENDER {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    #![allow(non_upper_case_globals)]
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let (config, config_path) = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => (Config::default(), None),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok((config, path)) => (config, Some(path)),
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    // Create renderer
    let mut renderer = QuadRenderer::new(&config, width, height);

    // Initialize glyph cache
    let glyph_cache = {
        println!("Initializing glyph cache");
        let init_start = ::std::time::Instant::now();

        let cache = renderer.with_loader(|mut api| {
            GlyphCache::new(rasterizer, &config, &mut api)
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);

        cache
    };

    let metrics = glyph_cache.font_metrics();
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(
        width as f32,
        height as f32,
        cell_width as f32,
        cell_height as f32
    );
    let pty_io = terminal.tty().reader();

    let (tx, rx) = mpsc::channel();
    unsafe {
        RESIZE_SENDER = Some(tx.clone());
    }

    let signal_flag = Flag::new(false);

    let terminal = Arc::new(FairMutex::new(terminal));
    let window = Arc::new(window);

    let event_loop = EventLoop::new(
        terminal.clone(),
        window.create_window_proxy(),
        signal_flag.clone(),
        pty_io,
    );

    let loop_tx = event_loop.channel();
    let event_loop_handle = event_loop.spawn(None);

    // Wraps a renderer and gives simple draw() api.
    let mut display = Display::new(
        window.clone(),
        renderer,
        glyph_cache,
        render_timer,
        rx
    );

    // Event processor
    let mut processor = event::Processor::new(
        input::LoopNotifier(loop_tx),
        terminal.clone(),
        tx
    );

    let (config_tx, config_rx) = mpsc::channel();

    // create a config watcher when config is loaded from disk
    let _config_reloader = config_path.map(|config_path| {
        config::Watcher::new(config_path, ConfigHandler {
            tx: config_tx,
            window: window.create_window_proxy(),
        })
    });

    // Main loop
    loop {
        // Wait for something to happen
        processor.process_events(&window);

        if let Ok(config) = config_rx.try_recv() {
            display.update_config(&config);
        }

        // Maybe draw the terminal
        let terminal = terminal.lock();
        signal_flag.set(false);
        if terminal.dirty {
            display.draw(terminal);
        }

        if process_should_exit() {
            break;
        }
    }

    // FIXME need file watcher to work with custom delegates before
    //       joining config reloader is possible
    // config_reloader.join().ok();

    // shutdown
    event_loop_handle.join().ok();
    println!("Goodbye");
}

struct ConfigHandler {
    tx: mpsc::Sender<config::Config>,
    window: ::glutin::WindowProxy,
}

impl config::OnConfigReload for ConfigHandler {
    fn on_config_reload(&mut self, config: Config) {
        if let Err(..) = self.tx.send(config) {
            err_println!("Failed to notify of new config");
            return;
        }

        self.window.wakeup_event_loop();
    }
}

struct Display {
    window: Arc<glutin::Window>,
    renderer: QuadRenderer,
    glyph_cache: GlyphCache,
    render_timer: bool,
    rx: mpsc::Receiver<(u32, u32)>,
    meter: Meter,
}

impl Display {
    pub fn update_config(&mut self, config: &Config) {
        self.renderer.update_config(config);
    }

    pub fn new(window: Arc<glutin::Window>,
               renderer: QuadRenderer,
               glyph_cache: GlyphCache,
               render_timer: bool,
               rx: mpsc::Receiver<(u32, u32)>)
               -> Display
    {
        Display {
            window: window,
            renderer: renderer,
            glyph_cache: glyph_cache,
            render_timer: render_timer,
            rx: rx,
            meter: Meter::new(),
        }
    }

    /// Draw the screen
    ///
    /// A reference to Term whose state is being drawn must be provided.
    ///
    /// This call may block if vsync is enabled
    pub fn draw(&mut self, mut terminal: MutexGuard<Term>) {
        terminal.dirty = false;

        // Resize events new_size and are handled outside the poll_events
        // iterator. This has the effect of coalescing multiple resize
        // events into one.
        let mut new_size = None;

        // Check for any out-of-band resize events (mac only)
        while let Ok(sz) = self.rx.try_recv() {
            new_size = Some(sz);
        }

        // Receive any resize events; only call gl::Viewport on last
        // available
        if let Some((w, h)) = new_size.take() {
            terminal.resize(w as f32, h as f32);
            self.renderer.resize(w as i32, h as i32);
        }

        {
            let glyph_cache = &mut self.glyph_cache;
            // Draw grid
            {
                let _sampler = self.meter.sampler();

                let size_info = terminal.size_info().clone();
                self.renderer.with_api(&size_info, |mut api| {
                    // Draw the grid
                    api.render_grid(&terminal.render_grid(), glyph_cache);
                });
            }

            // Draw render timer
            if self.render_timer {
                let timing = format!("{:.3} usec", self.meter.average());
                let color = ::term::cell::Color::Rgb(Rgb { r: 0xd5, g: 0x4e, b: 0x53 });
                self.renderer.with_api(terminal.size_info(), |mut api| {
                    api.render_string(&timing[..], glyph_cache, &color);
                });
            }
        }

        // Unlock the terminal mutex
        drop(terminal);
        self.window.swap_buffers().unwrap();
    }
}
<commit_msg>Dynamically update render_timer config<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// 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.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(core_intrinsics)]
#![allow(stable_features)] // lying about question_mark because 1.14.0 isn't released!

#![feature(proc_macro)]

#[macro_use]
extern crate serde_derive;

extern crate cgmath;
extern crate copypasta;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate mio;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;
extern crate vte;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod event;
mod event_loop;
mod index;
mod input;
mod meter;
mod renderer;
mod sync;
mod term;
mod tty;
mod util;
pub mod ansi;
pub mod config;
pub mod grid;

use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicBool, Ordering};

use parking_lot::{MutexGuard};

use event_loop::EventLoop;

use config::Config;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use sync::FairMutex;
use term::Term;
use tty::process_should_exit;

/// Channel used by resize handling on mac
static mut RESIZE_SENDER: Option<mpsc::Sender<(u32, u32)>> = None;

#[derive(Clone)]
pub struct Flag(Arc<AtomicBool>);
impl Flag {
    pub fn new(initial_value: bool) -> Flag {
        Flag(Arc::new(AtomicBool::new(initial_value)))
    }

    #[inline]
    pub fn get(&self) -> bool {
        self.0.load(Ordering::Acquire)
    }

    #[inline]
    pub fn set(&self, value: bool) {
        self.0.store(value, Ordering::Release)
    }
}

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = RESIZE_SENDER {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    #![allow(non_upper_case_globals)]
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let (config, config_path) = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => (Config::default(), None),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok((config, path)) => (config, Some(path)),
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    // Create renderer
    let mut renderer = QuadRenderer::new(&config, width, height);

    // Initialize glyph cache
    let glyph_cache = {
        println!("Initializing glyph cache");
        let init_start = ::std::time::Instant::now();

        let cache = renderer.with_loader(|mut api| {
            GlyphCache::new(rasterizer, &config, &mut api)
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);

        cache
    };

    let metrics = glyph_cache.font_metrics();
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(
        width as f32,
        height as f32,
        cell_width as f32,
        cell_height as f32
    );
    let pty_io = terminal.tty().reader();

    let (tx, rx) = mpsc::channel();
    unsafe {
        RESIZE_SENDER = Some(tx.clone());
    }

    let signal_flag = Flag::new(false);

    let terminal = Arc::new(FairMutex::new(terminal));
    let window = Arc::new(window);

    let event_loop = EventLoop::new(
        terminal.clone(),
        window.create_window_proxy(),
        signal_flag.clone(),
        pty_io,
    );

    let loop_tx = event_loop.channel();
    let event_loop_handle = event_loop.spawn(None);

    // Wraps a renderer and gives simple draw() api.
    let mut display = Display::new(
        window.clone(),
        renderer,
        glyph_cache,
        render_timer,
        rx
    );

    // Event processor
    let mut processor = event::Processor::new(
        input::LoopNotifier(loop_tx),
        terminal.clone(),
        tx
    );

    let (config_tx, config_rx) = mpsc::channel();

    // create a config watcher when config is loaded from disk
    let _config_reloader = config_path.map(|config_path| {
        config::Watcher::new(config_path, ConfigHandler {
            tx: config_tx,
            window: window.create_window_proxy(),
        })
    });

    // Main loop
    loop {
        // Wait for something to happen
        processor.process_events(&window);

        if let Ok(config) = config_rx.try_recv() {
            display.update_config(&config);
        }

        // Maybe draw the terminal
        let terminal = terminal.lock();
        signal_flag.set(false);
        if terminal.dirty {
            display.draw(terminal);
        }

        if process_should_exit() {
            break;
        }
    }

    // FIXME need file watcher to work with custom delegates before
    //       joining config reloader is possible
    // config_reloader.join().ok();

    // shutdown
    event_loop_handle.join().ok();
    println!("Goodbye");
}

struct ConfigHandler {
    tx: mpsc::Sender<config::Config>,
    window: ::glutin::WindowProxy,
}

impl config::OnConfigReload for ConfigHandler {
    fn on_config_reload(&mut self, config: Config) {
        if let Err(..) = self.tx.send(config) {
            err_println!("Failed to notify of new config");
            return;
        }

        self.window.wakeup_event_loop();
    }
}

struct Display {
    window: Arc<glutin::Window>,
    renderer: QuadRenderer,
    glyph_cache: GlyphCache,
    render_timer: bool,
    rx: mpsc::Receiver<(u32, u32)>,
    meter: Meter,
}

impl Display {
    pub fn update_config(&mut self, config: &Config) {
        self.renderer.update_config(config);
        self.render_timer = config.render_timer();
    }

    pub fn new(window: Arc<glutin::Window>,
               renderer: QuadRenderer,
               glyph_cache: GlyphCache,
               render_timer: bool,
               rx: mpsc::Receiver<(u32, u32)>)
               -> Display
    {
        Display {
            window: window,
            renderer: renderer,
            glyph_cache: glyph_cache,
            render_timer: render_timer,
            rx: rx,
            meter: Meter::new(),
        }
    }

    /// Draw the screen
    ///
    /// A reference to Term whose state is being drawn must be provided.
    ///
    /// This call may block if vsync is enabled
    pub fn draw(&mut self, mut terminal: MutexGuard<Term>) {
        terminal.dirty = false;

        // Resize events new_size and are handled outside the poll_events
        // iterator. This has the effect of coalescing multiple resize
        // events into one.
        let mut new_size = None;

        // Check for any out-of-band resize events (mac only)
        while let Ok(sz) = self.rx.try_recv() {
            new_size = Some(sz);
        }

        // Receive any resize events; only call gl::Viewport on last
        // available
        if let Some((w, h)) = new_size.take() {
            terminal.resize(w as f32, h as f32);
            self.renderer.resize(w as i32, h as i32);
        }

        {
            let glyph_cache = &mut self.glyph_cache;
            // Draw grid
            {
                let _sampler = self.meter.sampler();

                let size_info = terminal.size_info().clone();
                self.renderer.with_api(&size_info, |mut api| {
                    // Draw the grid
                    api.render_grid(&terminal.render_grid(), glyph_cache);
                });
            }

            // Draw render timer
            if self.render_timer {
                let timing = format!("{:.3} usec", self.meter.average());
                let color = ::term::cell::Color::Rgb(Rgb { r: 0xd5, g: 0x4e, b: 0x53 });
                self.renderer.with_api(terminal.size_info(), |mut api| {
                    api.render_string(&timing[..], glyph_cache, &color);
                });
            }
        }

        // Unlock the terminal mutex
        drop(terminal);
        self.window.swap_buffers().unwrap();
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::BufReader;
use std::io::BufRead;
use request::Request;
use turn::Turn;

mod request;
mod turn;

fn main()
{
	let input = io::stdin();
	let input = BufReader::new(input);
	let input = input.lines();
	let input = input
		.map(Result::unwrap)
		.filter(|s|s.len()>0)
		.map(|s|s.parse::<Request>())
		.map(Result::unwrap);

	for req in input
	{
		use request::Request::*;
		match req
		{
			ListSuperRegions(sregions) =>
			{
			},
			ListRegions(regions) =>
			{
			},
			ListNeighbours(relations) =>
			{
			},
			RequestStartingRegions(available) =>
			{
			},
			SettingNameYou(name) =>
			{
			},
			SettingNameOther(name) =>
			{
			},
			SettingArmies(count) =>
			{
			},
			UpdateMap(updates) =>
			{
			},
			TurnOther(turns) =>
			{
			},
			TurnPlace =>
			{
			},
			TurnArmies =>
			{
			},
		}
	}
}

<commit_msg>name setting<commit_after>use std::io;
use std::io::BufReader;
use std::io::BufRead;
use request::Request;
use turn::Turn;

mod request;
mod turn;

fn main()
{
	let input = io::stdin();
	let input = BufReader::new(input);
	let input = input.lines();
	let input = input
		.map(Result::unwrap)
		.filter(|s|s.len()>0)
		.map(|s|s.parse::<Request>())
		.map(Result::unwrap);

	let mut name_you = String::new();
	let mut name_other = String::new();

	for req in input
	{
		use request::Request::*;
		match req
		{
			ListSuperRegions(sregions) =>
			{
			},
			ListRegions(regions) =>
			{
			},
			ListNeighbours(relations) =>
			{
			},
			RequestStartingRegions(available) =>
			{
			},
			SettingNameYou(name) => name_you = name,
			SettingNameOther(name) => name_other = name,
			SettingArmies(count) =>
			{
			},
			UpdateMap(updates) =>
			{
			},
			TurnOther(turns) =>
			{
			},
			TurnPlace =>
			{
			},
			TurnArmies =>
			{
			},
		}
	}
}

<|endoftext|>


<commit_before>extern crate hyper;
extern crate toml;

extern crate rustc_serialize;
use rustc_serialize::json;

use std::io::{Read, Write};
use std::thread;

use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;

// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // TODO check that key actually fits in 8-bit int
    stacks: Vec<Vec<(i8,i64)>>,
    symbolSources: Vec<String>,
    version: u8,
}

fn main() {
    let address = "0.0.0.0:8080";

    println!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            let _ = req.read_to_string(&mut buffer);
            println!("DEBUG raw POST: {:?}", &buffer);
            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            println!("DEBUG decoded memoryMap: {:?}", decoded.memoryMap);
            println!("DEBUG decoded stacks: {:?}", decoded.stacks);
            println!("DEBUG decoded symbolSources: {:?}", decoded.symbolSources);
            println!("DEBUG decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            let symbols = client(symbol_url, decoded.memoryMap);
            let _ = res.write_all(symbols.as_bytes());

            res.end().unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>) -> String {
    let mut handles = vec![];
    for (debug_file, debug_id) in memory_map {
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        // TODO decide min/max possible threads, possibly based on number of cores?
        // TODO most of the time is spent waiting on I/O, maybe async would be more appropriate?
        handles.push(thread::spawn(move || {
            let c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let mut body = String::new();
            let _ = res.read_to_string(&mut body);

            (symbol_file, body)
        }));
    }

    let mut result = String::new();

    for handle in handles {
        let (symbol_file, body) = handle.join().unwrap();
        for c in format!("{:?} {:?}\n", symbol_file, body).chars() {
            result.push(c);
        }
    }

    result
}

fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>comment<commit_after>extern crate hyper;
extern crate toml;

extern crate rustc_serialize;
use rustc_serialize::json;

use std::io::{Read, Write};
use std::thread;

use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;

// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // TODO check that key actually fits in 8-bit int
    stacks: Vec<Vec<(i8,i64)>>,
    symbolSources: Vec<String>,
    version: u8,
}

fn main() {
    let address = "0.0.0.0:8080";

    println!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            let _ = req.read_to_string(&mut buffer);
            println!("DEBUG raw POST: {:?}", &buffer);
            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            println!("DEBUG decoded memoryMap: {:?}", decoded.memoryMap);
            println!("DEBUG decoded stacks: {:?}", decoded.stacks);
            println!("DEBUG decoded symbolSources: {:?}", decoded.symbolSources);
            println!("DEBUG decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");
            let symbols = client(symbol_url, decoded.memoryMap);
            let _ = res.write_all(symbols.as_bytes());

            res.end().unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>) -> String {
    let mut handles = vec![];
    for (debug_file, debug_id) in memory_map {
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        // TODO decide min/max possible threads, possibly based on number of cores?
        // TODO most of the time is spent waiting on I/O, maybe async would be more appropriate?
        handles.push(thread::spawn(move || {
            let c = Client::new();
            let mut res = c.get(&this_url).send().unwrap();
            let mut body = String::new();
            let _ = res.read_to_string(&mut body);

            (symbol_file, body)
        }));
    }

    let mut result = String::new();

    for handle in handles {
        let (symbol_file, body) = handle.join().unwrap();
        for c in format!("{:?} {:?}\n", symbol_file, body).chars() {
            result.push(c);
        }
    }

    result
}

fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let &'static toml = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;

enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop()),
            Bulltin::plus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 + n2);
            },
            Bulltin::minus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 - n2);
            },
            Bulltin::times => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 * n2);
            },
            Bulltin::divide => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone)]
enum Word {
    custom(Rc<Vec<String>>),
    builtin(Builtin),
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
    int(i32),
    float(f32),
}

enum Branch {
    custom(Rc<Vec<String>>)
    builtin(Builtin),
    ifelse(Vec<String>, Vec<String>),
    dotquote(<String>)
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.get(name) {
            Some(word)
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        dict = HashMap::new();
        dict.insert(".\"", Word::dotquote);
        dict.insert(".", Word::builtin(Builtin::dot));
        dict.insert("+", Word::builtin(Builtin::plus));
        dict.insert("-", Word::builtin(Buitlin::minus));
        dict.insert("*", Word::builtin(Buitlin::times));
        dict.insert("/", Word::builtin(Buitlin::divide));
        dict.insert(":", Word::colon);
        dict.insert(";", Word::semicolon);
        dict.insert("if", Word::_if);
        dict.insert("then", Word::_then);
        dict.insert("else", Word::_else);
        Dictionary{items: dict}
    }
}

fn parse_next_word(code: &Chars, dict: &Dictionary) -> Some(Branch) {
    let mut word_str = String::new();
    let mut branch = None;
    while let Some(c) = chars.next() {
        if c == ' ' || c == '\n' {
            if let Some(word) = dict.get(word_str) {
                match word {
                    //stop => { return (words, true); },
                    Word::custom(_) | Word::builtin(_) | Word::int(_) | Word::float(_) => {
                        branch = Some(Branch::word(word.clone()))
                    }
                    Word::dotquote => {
                        let text = chars.take_while(|x| x != '"').collect();
                        branch = Branch::dotquote(text);
                    }
                    Word::colon => {
                        let mut name = String::new();
                        while let Some(c) = chars.next() {
                            if c == ' ' || c == '\n' {
                                break;
                            }
                            name.push(c);
                        }

                    }
                }
            break;
            }
        } else {
            word_str.push(c);
        }
    }

    branch
}

fn parse(code: &Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut word_str = String::new();
    let dict = Dictionary::default();
    while let Some(branch) = parse_next_word(code, &Dictionary) {
        branches.push(branch);
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&code.iter(), None).0);
}
<commit_msg>Some changes; still not usable<commit_after>use std::env;
use std::io;
use std::io::prelude::*;
use std::fs::File;
use std::str::Chars;

enum Builtin {
    dot,
    plus,
    minus,
    times,
    divide,
}

impl Builtin {
    fn call(&self, stack: &Vec<i32>) {
        match *self {
            Builtin::dot => print!("{}", stack.pop()),
            Bulltin::plus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 + n2);
            },
            Bulltin::minus => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 - n2);
            },
            Bulltin::times => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 * n2);
            },
            Bulltin::divide => {
                n2 = stack.pop();
                n1 = stack.pop();
                stack.push(n1 / n2);
            },
        }
    }
}

#[derive(Clone)]
enum Word {
    custom(Rc<Vec<Word>>),
    builtin(Builtin),
    colon,
    semicolon,
    _if,
    then,
    _else,
    dotquote,
    int(i32),
    float(f32),
}

enum Branch {
    custom(Rc<Vec<Word>>)
    builtin(Builtin),
    ifelse(Vec<Word>, Vec<Word>),
    dotquote(<String>)
    int(i32),
    float(f32),
}

struct Dictionary {
    items: HashMap<String, Word>
}

impl Dictionary {
    fn get(&self, name: &str) -> Option<Word> {
        if let Some(word) = self.get(name) {
            Some(word)
        } else if let Ok(num) = name.parse::<i32>() {
            Some(Word::int(num))
        } else if let Ok(num) = name.parse::<f32>() {
            Some(Word::float(num))
        } else {
            None
        }
    }
    fn set(&self, name: &str, value: Word) {
        if self.items.contains_key(name) {
            // XXX display debug message another way
            println!("Redefined {}", name);
        }
    }
}

impl Default for Dictionary {
    fn default() -> Dictionary {
        dict = HashMap::new();
        dict.insert(".\"", Word::dotquote);
        dict.insert(".", Word::builtin(Builtin::dot));
        dict.insert("+", Word::builtin(Builtin::plus));
        dict.insert("-", Word::builtin(Buitlin::minus));
        dict.insert("*", Word::builtin(Buitlin::times));
        dict.insert("/", Word::builtin(Buitlin::divide));
        dict.insert(":", Word::colon);
        dict.insert(";", Word::semicolon);
        dict.insert("if", Word::_if);
        dict.insert("then", Word::_then);
        dict.insert("else", Word::_else);
        Dictionary{items: dict}
    }
}

fn next_word(chars: &Chars) -> Some(String) {
    let mut word = String::new();

    while let Some(c) = chars.next() {
        if (c == ' ' || c == '\n') && !string.is_empty() {
            break;
        }
        string.push(c);
    }

    if string.is_empty() {
        // No input left
        None
    else {
        Some(string)
    }
}

fn parse_next_word(word_str: &str, chars: &Chars, dict: &Dictionary) -> Branch {
    if let Some(word) = dict.get(word_str) {
        match word {
            //stop => { return (words, true); },
            Word::custom(_) | Word::builtin(_) | Word::int(_) | Word::float(_) => {
                Branch::word(word.clone())
            }
            Word::dotquote => {
                let text = chars.take_while(|x| x != '"').collect();
                Branch::dotquote(text)
            }
            Word::colon => {
                let name = next_word(chars).unwrap();

            }
        }
    } else {
        panic!("Word not in dictionary.");
    }
}

fn parse(code: &Chars) -> Vec<Branch> {
    let mut branches: Vec<Branch> = Vec::new();
    let mut word_str = String::new();
    let dict = Dictionary::default();
    while let Some(word_str) = next_word(code) {
        branches.push(parse_word(&word_str, code, dict));
    }

    branches
}

fn main() {
    let path = env::args().nth(1).unwrap();
    let mut file = File::open(&path).unwrap();
    let mut code = String::new();
    file.read_to_string(&mut code).unwrap();

    println!("{:#?}", parse(&code.iter(), None).0);
}
<|endoftext|>


<commit_before>extern crate clap;
use clap::{Arg, App, AppSettings};
use slog::{Logger, LevelFilter, Level, DrainExt};
use yaml_rust::{YamlLoader};
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::Yaml;
use model::*;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stdlog;
extern crate yaml_rust;

#[cfg(test)]
extern crate spectral;

mod model;
mod parser;

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => format!("{:?}", e));
      panic!(1)
    }
  };

  let yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => format!("{:?}", e));
      panic!(2)
    }
  };
  let yaml_config = &yaml_documents[0];
  let dot_files: &Yaml = &yaml_config["files"];
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  }

  info!(log, "Parsed config file. Liftoff!");
  for dot_file in parser::parse_dot_files(&log, dot_files) {
    process_dot_file(&log, dot_file);
  }
}

fn process_dot_file(log: &Logger, dot_file: DotFile) {
  let log = log.new(o!("target" => dot_file.target, "source" => dot_file.source, "type" => format!("{:?}", dot_file.dot_file_type)));
  info!(log, "Process entry");
}

fn load_config_file(file: &str) -> Result<String, std::io::Error> {
  let mut file = try!(File::open(file));
  let mut content = String::new();
  try!(file.read_to_string(&mut content));
  return Ok(content)
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version("0.1")
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force").short("f").long("force").takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
}

<commit_msg>check source path existance<commit_after>extern crate clap;
use clap::{Arg, App, AppSettings};
use slog::{Logger, LevelFilter, Level, DrainExt};
use yaml_rust::{YamlLoader};
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::Yaml;
use model::*;
use std::path::Path;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_stdlog;
extern crate yaml_rust;

#[cfg(test)]
extern crate spectral;

mod model;
mod parser;

fn main() {
  let matches: clap::ArgMatches = create_app().get_matches();
  let stream = slog_term::streamer().full().build();
  let verbose: bool = matches.is_present("verbose");
  let log = if verbose {
    slog::Logger::root(stream.fuse(), o!())
  } else {
    slog::Logger::root(LevelFilter::new(stream, Level::Info).fuse(), o!())
  };

  let config_file = matches.value_of("config_file").unwrap();
  info!(log, "Starting engine"; "config_file" => config_file);

  let config = match load_config_file(config_file) {
    Ok(content) => content,
    Err(e) => {
      error!(log, "Failed to load config file."; "error" => format!("{:?}", e));
      panic!(1)
    }
  };

  let yaml_documents = match YamlLoader::load_from_str(&config) {
    Ok(yaml) => yaml,
    Err(e) => {
      error!(log, "Failed to parse config file."; "error" => format!("{:?}", e));
      panic!(2)
    }
  };
  let yaml_config = &yaml_documents[0];
  let dot_files: &Yaml = &yaml_config["files"];
  if dot_files.is_badvalue() {
    warn!(log, "Empty files list");
  }

  info!(log, "Parsed config file. Liftoff!");
  for dot_file in parser::parse_dot_files(&log, dot_files) {
    process_dot_file(&log, dot_file);
  }
}

fn process_dot_file(log: &Logger, dot_file: DotFile) {
  let log = log.new(o!("target" => dot_file.target.clone(), "source" => dot_file.source.clone(), "type" => format!("{:?}", dot_file.dot_file_type)));
  info!(log, "Process entry");
  let source_path = Path::new(&dot_file.source);
  if(!source_path.exists()) {
    warn!(log, "Source path does not exist");
    return;
  }
}

fn load_config_file(file: &str) -> Result<String, std::io::Error> {
  let mut file = try!(File::open(file));
  let mut content = String::new();
  try!(file.read_to_string(&mut content));
  return Ok(content)
}

fn create_app<'a>() -> App<'a, 'a> {
  App::new("dotcopter")
    .version("0.1")
    .setting(AppSettings::ColoredHelp)
    .author("Patrick Haun <bomgar85@googlemail.com>")
    .about("manages dotfiles installation")
    .arg(Arg::with_name("force").short("f").long("force").takes_value(false))
    .arg(Arg::with_name("verbose")
           .long("verbose")
           .short("v")
           .help("debug output")
           .required(false)
           .takes_value(false))
    .arg(Arg::with_name("config_file").required(true))
}

<|endoftext|>


<commit_before>//! Coredump implementation of the Space API server.
//!
//! Start this via the commandline:
//!
//!     ./coredump_status [-p PORT] [-i IP]
extern crate env_logger;
extern crate docopt;
extern crate rustc_serialize;
extern crate spaceapi_server;

mod utils;

use std::sync::{Arc, Mutex};
use docopt::Docopt;
use spaceapi_server::SpaceapiServer;
use spaceapi_server::api;
use spaceapi_server::api::sensors::{TemperatureSensorTemplate, PeopleNowPresentSensorTemplate};
use spaceapi_server::api::Optional::{Value, Absent};
use spaceapi_server::datastore::{DataStore, RedisStore};
use utils::Ipv4;

static USAGE: &'static str = "
Usage: coredump_status [-p PORT] [-i IP]

Options:
    -p PORT  The port to listen on [default: 3000].
    -i IP    The ipv4 address to listen on [default: 127.0.0.1].
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_p: u16,
    flag_i: Ipv4,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    env_logger::init().unwrap();

    // Parse arguments
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode())
                                       .unwrap_or_else(|e| e.exit());
    let host = args.flag_i.ip;
    let port = args.flag_p;

    // Create new Status instance
    let mut status = api::Status::new(
        "coredump",
        "https://www.coredump.ch/logo.png",
        "https://www.coredump.ch/",
        api::Location {
            address: Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".into()),
            lat: 47.22936,
            lon: 8.82949,
        },
        api::Contact {
            irc: Value("irc://freenode.net/#coredump".into()),
            twitter: Value("@coredump_ch".into()),
            foursquare: Value("525c20e5498e875d8231b1e5".into()),
            email: Value("vorstand@lists.coredump.ch".into()),
        },
        vec![
            "email".into(),
            "twitter".into(),
        ],
    );

    // Add optional data
    status.spacefed = Value(api::Spacefed {
        spacenet: false,
        spacesaml: false,
        spacephone: false,
    });
    status.feeds = Value(api::Feeds {
        blog: Value(api::Feed {
            _type: Value("rss".into()),
            url: "https://www.coredump.ch/feed/".into(),
        }),
        wiki: Absent,
        calendar: Absent,
        flickr: Absent,
    });
    status.projects = Value(vec![
        "https://www.coredump.ch/projekte/".into(),
        "https://discourse.coredump.ch/c/projects".into(),
        "https://github.com/coredump-ch/".into(),
    ]);
    status.state.message = Value("Open every Monday from 20:00".into());

    // Set up datastore
    let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

    // Set up server
    let mut server = SpaceapiServer::new(host, port, status, datastore);

    // Register sensors
    server.register_sensor(Box::new(TemperatureSensorTemplate {
        unit: "°C".into(),
        location: "Hackerspace".into(),
        name: Value("Raspberry CPU".into()),
        description: Absent,
    }), "raspi_temperature".into());
    server.register_sensor(Box::new(TemperatureSensorTemplate {
        unit: "°C".into(),
        location: "Hackerspace".into(),
        name: Value("Room Temperature".into()),
        description: Absent,
    }), "room_temperature".into());
    server.register_sensor(Box::new(PeopleNowPresentSensorTemplate {
        location: Value("Hackerspace".into()),
        name: Absent,
        description: Absent,
        names: Absent,
    }), "people_present".into());

    // Serve!
    server.serve();
}
<commit_msg>Add webcam URLs<commit_after>//! Coredump implementation of the Space API server.
//!
//! Start this via the commandline:
//!
//!     ./coredump_status [-p PORT] [-i IP]
extern crate env_logger;
extern crate docopt;
extern crate rustc_serialize;
extern crate spaceapi_server;

mod utils;

use std::sync::{Arc, Mutex};
use docopt::Docopt;
use spaceapi_server::SpaceapiServer;
use spaceapi_server::api;
use spaceapi_server::api::sensors::{TemperatureSensorTemplate, PeopleNowPresentSensorTemplate};
use spaceapi_server::api::Optional::{Value, Absent};
use spaceapi_server::datastore::{DataStore, RedisStore};
use utils::Ipv4;

static USAGE: &'static str = "
Usage: coredump_status [-p PORT] [-i IP]

Options:
    -p PORT  The port to listen on [default: 3000].
    -i IP    The ipv4 address to listen on [default: 127.0.0.1].
";

#[derive(RustcDecodable, Debug)]
struct Args {
    flag_p: u16,
    flag_i: Ipv4,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    env_logger::init().unwrap();

    // Parse arguments
    let args: Args = Docopt::new(USAGE).and_then(|d| d.decode())
                                       .unwrap_or_else(|e| e.exit());
    let host = args.flag_i.ip;
    let port = args.flag_p;

    // Create new Status instance
    let mut status = api::Status::new(
        "coredump",
        "https://www.coredump.ch/logo.png",
        "https://www.coredump.ch/",
        api::Location {
            address: Value("Spinnereistrasse 2, 8640 Rapperswil, Switzerland".into()),
            lat: 47.22936,
            lon: 8.82949,
        },
        api::Contact {
            irc: Value("irc://freenode.net/#coredump".into()),
            twitter: Value("@coredump_ch".into()),
            foursquare: Value("525c20e5498e875d8231b1e5".into()),
            email: Value("vorstand@lists.coredump.ch".into()),
        },
        vec![
            "email".into(),
            "twitter".into(),
        ],
    );

    // Add optional data
    status.spacefed = Value(api::Spacefed {
        spacenet: false,
        spacesaml: false,
        spacephone: false,
    });
    status.feeds = Value(api::Feeds {
        blog: Value(api::Feed {
            _type: Value("rss".into()),
            url: "https://www.coredump.ch/feed/".into(),
        }),
        wiki: Absent,
        calendar: Absent,
        flickr: Absent,
    });
    status.projects = Value(vec![
        "https://www.coredump.ch/projekte/".into(),
        "https://discourse.coredump.ch/c/projects".into(),
        "https://github.com/coredump-ch/".into(),
    ]);
    status.cam = Value(vec![
        "https://webcam.coredump.ch/cams/ultimaker.png".into(),
    ]);
    status.state.message = Value("Open every Monday from 20:00".into());

    // Set up datastore
    let datastore = Arc::new(Mutex::new(Box::new(RedisStore::new().unwrap()) as Box<DataStore>));

    // Set up server
    let mut server = SpaceapiServer::new(host, port, status, datastore);

    // Register sensors
    server.register_sensor(Box::new(TemperatureSensorTemplate {
        unit: "°C".into(),
        location: "Hackerspace".into(),
        name: Value("Raspberry CPU".into()),
        description: Absent,
    }), "raspi_temperature".into());
    server.register_sensor(Box::new(TemperatureSensorTemplate {
        unit: "°C".into(),
        location: "Hackerspace".into(),
        name: Value("Room Temperature".into()),
        description: Absent,
    }), "room_temperature".into());
    server.register_sensor(Box::new(PeopleNowPresentSensorTemplate {
        location: Value("Hackerspace".into()),
        name: Absent,
        description: Absent,
        names: Absent,
    }), "people_present".into());

    // Serve!
    server.serve();
}
<|endoftext|>


<commit_before>
#![allow(dead_code)]

extern crate gl;
extern crate glutin;
extern crate cable_math;
#[macro_use]
extern crate gondola_vertex_macro;

mod framebuffer;
mod color;
mod texture;
mod shader;
mod buffer;
mod vertex_array;
mod matrix_stack;

use glutin::*;
use framebuffer::*;
use color::*;
use shader::*;
use buffer::*;
use vertex_array::*;
use matrix_stack::*;

use gl::types::*;
use std::time::{Instant, Duration};

const VERTEX_SOURCE: &'static str = "
    #version 330 core

    // Inputs are automatically inserted

    out vec4 vert_color;

    uniform mat4 mvp;

    void main() {
        gl_Position = mvp * vec4(position, 0.0, 1.0);
        vert_color = color;
    }
";
const FRAGMENT_SOURCE: &'static str = "
    #version 330 core

    in vec4 vert_color;
    out vec4 out_color;

    void main() {
        out_color = vert_color;
    }
";

#[derive(Vertex)]
struct TestVertex {
    position: (f32, f32),
    color: Color,
}
impl TestVertex {
    fn new(x: f32, y: f32) -> TestVertex {
        TestVertex {
            position: (x, y),
            color: Color::hex("ff00aa"),
        }
    }
}

fn main() {
    use cable_math::Vec2;
    let a = Vec2::new(0, 10);
    let b = Vec2::new(3, 10);
    println!("{}", a + b);

//    let clear_color = Color::hex("ff34aa");
    let clear_color = Color::hex("00ff00");
    let clear_color = clear_color.with_lightness(4.0);

    let window = glutin::Window::new().unwrap();

    unsafe {
        window.make_current().unwrap();
        gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    }

    let mut mouse_pos: (u32, u32) = (0, 0);
    let mut window_size = window.get_inner_size_points().unwrap();
    unsafe {
        gl::Viewport(0, 0, window_size.0 as i32, window_size.1 as i32);
    }

    let mut framebuffer = FramebufferProperties::new(window_size.0, window_size.1) .build().unwrap();

    let shader = Shader::with_vertex::<TestVertex>(VERTEX_SOURCE, None, Some(FRAGMENT_SOURCE)).unwrap();
    shader.bind();

    let mut vbo = PrimitiveBuffer::new(BufferTarget::Array, BufferUsage::StaticDraw, DataType::Float);
    let vao = VertexArray::new();
    vao.add_data_source(&vbo, 0, 2, 2, 0);

    let test_data = vec![
        TestVertex::new(0.0, 0.0),
        TestVertex::new(100.0, 0.0),
        TestVertex::new(0.0, 100.0),
    ];
    let mut vertex_buffer = VertexBuffer::from_data(PrimitiveMode::Triangles, &test_data);

    let mut line_buffer = VertexBuffer::new(PrimitiveMode::LineStrip, BufferUsage::StaticDraw);

    let mut matrix_stack = MatrixStack::new();

    let mut delta: u64 = 16;
    let target_delta = Duration::from_millis(14);

    'main_loop:
    loop {
        let start_time = Instant::now();

        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main_loop,
                Event::Resized(width, height) => {
                    window_size = (width, height);
                    framebuffer = FramebufferProperties::new(window_size.0, window_size.1).build().unwrap();
                    matrix_stack.ortho(0.0, window_size.0 as f32, 0.0, window_size.1 as f32, -1.0, 1.0);
                    unsafe {
                        gl::Viewport(0, 0, window_size.0 as i32, window_size.1 as i32);
                    }
                },
                Event::MouseMoved(x, y) => {
                    mouse_pos = (x as u32, window_size.1 - y as u32);
                },
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    println!("Mouse pressed");
                    line_buffer.put_at_end(&[TestVertex::new(mouse_pos.0 as f32, mouse_pos.1 as f32)]);
                },
                e => println!("{:?}", e)
            }
        }

        matrix_stack.ortho(0.0, window_size.0 as f32, 0.0, window_size.1 as f32, -1.0, 1.0);
        shader.set_uniform("mvp", matrix_stack.mvp());

        let new_data = vec![
            0.0, 0.0,
            100.0, 0.0,
            mouse_pos.0 as f32, mouse_pos.1 as f32
        ];
        vbo.put_floats(new_data);

        vertex_buffer.put(0, &[TestVertex::new(mouse_pos.0 as f32, mouse_pos.1 as f32)]);

        framebuffer.bind();
        framebuffer::clear(&clear_color);
        vertex_buffer.draw();
        line_buffer.draw();
        vao.draw(PrimitiveMode::Triangles, 0..3);
        framebuffer.blit();

        window.swap_buffers().unwrap();

        // Ensure loop runs at aprox. target delta
        let elapsed = start_time.elapsed();
        if elapsed < target_delta {
            std::thread::sleep(target_delta - elapsed); // This is not very precice :/
        }
        let delta_dur = start_time.elapsed();
        delta = delta_dur.as_secs()*1000 + (delta_dur.subsec_nanos() as u64)/1000000;
    }
}

<commit_msg>Removed dumb test code<commit_after>
#![allow(dead_code)]

extern crate gl;
extern crate glutin;
extern crate cable_math;
#[macro_use]
extern crate gondola_vertex_macro;

mod framebuffer;
mod color;
mod texture;
mod shader;
mod buffer;
mod vertex_array;
mod matrix_stack;

use glutin::*;
use framebuffer::*;
use color::*;
use shader::*;
use buffer::*;
use vertex_array::*;
use matrix_stack::*;

use gl::types::*;
use std::time::{Instant, Duration};

const VERTEX_SOURCE: &'static str = "
    #version 330 core

    // Inputs are automatically inserted

    out vec4 vert_color;

    uniform mat4 mvp;

    void main() {
        gl_Position = mvp * vec4(position, 0.0, 1.0);
        vert_color = color;
    }
";
const FRAGMENT_SOURCE: &'static str = "
    #version 330 core

    in vec4 vert_color;
    out vec4 out_color;

    void main() {
        out_color = vert_color;
    }
";

#[derive(Vertex)]
struct TestVertex {
    position: (f32, f32),
    color: Color,
}
impl TestVertex {
    fn new(x: f32, y: f32) -> TestVertex {
        TestVertex {
            position: (x, y),
            color: Color::hex("ff00aa"),
        }
    }
}

fn main() {
//    let clear_color = Color::hex("ff34aa");
    let clear_color = Color::hex("00ff00");
    let clear_color = clear_color.with_lightness(4.0);

    let window = glutin::Window::new().unwrap();

    unsafe {
        window.make_current().unwrap();
        gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    }

    let mut mouse_pos: (u32, u32) = (0, 0);
    let mut window_size = window.get_inner_size_points().unwrap();
    unsafe {
        gl::Viewport(0, 0, window_size.0 as i32, window_size.1 as i32);
    }

    let mut framebuffer = FramebufferProperties::new(window_size.0, window_size.1) .build().unwrap();

    let shader = Shader::with_vertex::<TestVertex>(VERTEX_SOURCE, None, Some(FRAGMENT_SOURCE)).unwrap();
    shader.bind();

    let mut vbo = PrimitiveBuffer::new(BufferTarget::Array, BufferUsage::StaticDraw, DataType::Float);
    let vao = VertexArray::new();
    vao.add_data_source(&vbo, 0, 2, 2, 0);

    let test_data = vec![
        TestVertex::new(0.0, 0.0),
        TestVertex::new(100.0, 0.0),
        TestVertex::new(0.0, 100.0),
    ];
    let mut vertex_buffer = VertexBuffer::from_data(PrimitiveMode::Triangles, &test_data);

    let mut line_buffer = VertexBuffer::new(PrimitiveMode::LineStrip, BufferUsage::StaticDraw);

    let mut matrix_stack = MatrixStack::new();

    let mut delta: u64 = 16;
    let target_delta = Duration::from_millis(14);

    'main_loop:
    loop {
        let start_time = Instant::now();

        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main_loop,
                Event::Resized(width, height) => {
                    window_size = (width, height);
                    framebuffer = FramebufferProperties::new(window_size.0, window_size.1).build().unwrap();
                    matrix_stack.ortho(0.0, window_size.0 as f32, 0.0, window_size.1 as f32, -1.0, 1.0);
                    unsafe {
                        gl::Viewport(0, 0, window_size.0 as i32, window_size.1 as i32);
                    }
                },
                Event::MouseMoved(x, y) => {
                    mouse_pos = (x as u32, window_size.1 - y as u32);
                },
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    println!("Mouse pressed");
                    line_buffer.put_at_end(&[TestVertex::new(mouse_pos.0 as f32, mouse_pos.1 as f32)]);
                },
                e => println!("{:?}", e)
            }
        }

        matrix_stack.ortho(0.0, window_size.0 as f32, 0.0, window_size.1 as f32, -1.0, 1.0);
        shader.set_uniform("mvp", matrix_stack.mvp());

        let new_data = vec![
            0.0, 0.0,
            100.0, 0.0,
            mouse_pos.0 as f32, mouse_pos.1 as f32
        ];
        vbo.put_floats(new_data);

        vertex_buffer.put(0, &[TestVertex::new(mouse_pos.0 as f32, mouse_pos.1 as f32)]);

        framebuffer.bind();
        framebuffer::clear(&clear_color);
        vertex_buffer.draw();
        line_buffer.draw();
        vao.draw(PrimitiveMode::Triangles, 0..3);
        framebuffer.blit();

        window.swap_buffers().unwrap();

        // Ensure loop runs at aprox. target delta
        let elapsed = start_time.elapsed();
        if elapsed < target_delta {
            std::thread::sleep(target_delta - elapsed); // This is not very precice :/
        }
        let delta_dur = start_time.elapsed();
        delta = delta_dur.as_secs()*1000 + (delta_dur.subsec_nanos() as u64)/1000000;
    }
}

<|endoftext|>


<commit_before>extern crate unicode_width;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::cmp::min;
use std::collections::HashSet;
use std::env;
use std::io::{stdin, BufRead};
use std::process;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;
use unicode_width::UnicodeWidthStr;

#[cfg(windows)] pub const NEWLINE: &'static str = "\r\n";
#[cfg(not(windows))] pub const NEWLINE: &'static str = "\n";

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
const TIMESTAMP: &'static str = include_str!(concat!(env!("OUT_DIR"), "/timestamp.txt"));
const TARGET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/target.txt"));
const COMMIT: &'static str = include_str!(concat!(env!("OUT_DIR"), "/commit.txt"));

fn main() {
    env::set_var("RUST_BACKTRACE", "1");
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            process::exit(1);
        },
    };

    if args.help { return; }

    if args.version {
        if COMMIT == "" {
            println!("heatseeker {} (built {} for {})", VERSION, TIMESTAMP, TARGET);
        } else {
            println!("heatseeker {}-{} (built {} for {})", VERSION, COMMIT, TIMESTAMP, TARGET);
        }
        return;
    }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search, args.filter_only);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        let desired_rows = if args.full_screen { 999 } else { 20 };
        let selections = event_loop(desired_rows, &choices, &initial_search, args.filter_only);
        print!("{}", selections);
    }
}

fn event_loop(desired_rows: u16, choices: &[&str], initial_search: &str, filter_only: bool) -> String {
    let mut search = Search::new(choices, initial_search.to_string(), filter_only);
    let mut screen = screen::new(desired_rows);

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(screen.as_mut(), &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in &keys {
            handle_key(&mut search, key, screen.visible_choices());
        }
    }

    screen.blank_screen();
    search.get_selections()
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace |
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('r') => std::panic!("This is a test backtrace"),
        Control('c') |
        Control('g') => search.cancel(),
        Control('t') => { search.toggle_selection(); search.down(visible_choices); },
        Control('p') |
        Up |
        ShiftTab => search.up(visible_choices),
        Control('n') |
        Down |
        Tab => search.down(visible_choices),
        Home => search.home(),
        End => search.end(visible_choices),
        Enter => search.done(),
        PgUp => search.pgup(visible_choices),
        PgDown => search.pgdown(visible_choices),
        _ => {},
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    scroll_offset: usize,
    cursor_index: usize,
    state: SearchState,
    selections: HashSet<String>,
    filter_only: bool,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String, filter_only: bool) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            scroll_offset: 0,
            cursor_index: 0,
            state: InProgress,
            selections: HashSet::new(),
            filter_only: filter_only,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let match_count = self.matches.len();
        let limit = min(visible_choices as usize - 1, match_count - 1);
        let should_wrap = self.scroll_offset == 0;
        if self.cursor_index == 0 {
            if should_wrap {
                if match_count > visible_choices as usize {
                    self.scroll_offset = match_count - visible_choices as usize;
                    self.cursor_index = visible_choices as usize - 1;
                } else {
                    self.cursor_index = limit;
                }
            } else {
                self.scroll_offset -= 1;
            }
        } else {
            self.cursor_index -= 1;
        }
    }

    fn down(&mut self, visible_choices: u16) {
        let match_count = self.matches.len();
        let limit = min(visible_choices as usize - 1, match_count - 1);
        let should_wrap = self.cursor_index + self.scroll_offset == match_count - 1;
        if self.cursor_index == limit {
            if should_wrap {
                self.cursor_index = 0;
                self.scroll_offset = 0;
            } else {
                self.scroll_offset += 1;
            }
        } else {
            self.cursor_index += 1;
        }
    }

    fn home(&mut self) {
        self.cursor_index = 0;
        self.scroll_offset = 0;
    }

    fn end(&mut self, visible_choices: u16) {
        self.home();
        self.up(visible_choices);
    }

    fn pgup(&mut self, visible_choices: u16) {
        for _ in 0..visible_choices {
            if self.scroll_offset == 0 && self.cursor_index == 0 {
                return;
            }
            self.up(visible_choices);
        }
    }

    fn pgdown(&mut self, visible_choices: u16) {
        for _ in 0..visible_choices {
            if self.scroll_offset + self.cursor_index == self.matches.len() - 1 {
                return;
            }
            self.down(visible_choices);
        }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.cursor_index = 0;
        self.scroll_offset = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.cursor_index = 0;
        self.scroll_offset = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.cursor_index = 0;
        self.scroll_offset = 0;
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query, self.filter_only);
            self.stale = false;
        }
    }

    fn toggle_selection(&mut self) {
        self.recompute_matches();
        let selection = self.matches.get(self.scroll_offset + self.cursor_index).unwrap_or(&"").to_string();
        if self.selections.contains(&selection) {
            self.selections.remove(&selection);
        } else {
            self.selections.insert(selection);
        }
    }

    fn get_selections(&mut self) -> String {
        let mut ret = String::new();
        if self.state != Canceled {
            for selection in &self.selections {
                ret.push_str(selection);
                ret.push_str(NEWLINE);
            }
            if ret.is_empty() {
                self.recompute_matches();
                let selection = self.matches.get(self.scroll_offset + self.cursor_index).unwrap_or(&"").to_string();
                ret.push_str(&selection);
            }
        }
        ret
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut dyn Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices){}", search.query, search.matches.len(), search.choices.len(), NEWLINE));

    print_matches(screen, &search.matches, &search.query, search.scroll_offset, search.cursor_index, &search.selections);

    let query_str: &str = &search.query;
    screen.move_cursor_to_prompt_line(2 + UnicodeWidthStr::width(query_str) as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut dyn Screen, matches: &[&str], query: &str, scroll_offset: usize, cursor_index: usize, selections: &HashSet<String>) {
    let mut i = 1;
    for choice in matches[scroll_offset..].iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width();
        let mut annotated_choice = choice.to_string();
        if selections.contains(&annotated_choice) {
            annotated_choice.push(' ');
            if cfg!(windows) {
                annotated_choice.push('√');
            } else {
                annotated_choice.push('✓');
            }
        }
        print_match(&annotated_choice, &indices, max_width, &mut |s, highlight| {
            if i == cursor_index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else if highlight {
                screen.write_red(s);
            } else { screen.write(s); }
        });
        if i >= screen.visible_choices() as usize {
            return;
        }
        screen.write(NEWLINE);
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut dyn FnMut(&str, bool)) {
    #[cfg(windows)] const MARGIN: u16 = 1;
    #[cfg(not(windows))] const MARGIN: u16 = 0;
    let max_width = max_width - MARGIN;
    let chars_in_choice = choice.chars().count();
    let mut chars_to_draw = min(chars_in_choice, max_width as usize);
    while UnicodeWidthStr::width(slice_chars(choice, 0, chars_to_draw)) > max_width as usize {
        chars_to_draw -= 1;
    }
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(slice_chars(choice, last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(slice_chars(choice, idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(slice_chars(choice, last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let stdin = stdin();
    let mut lines = Vec::new();

    let mut stdin = stdin.lock();
    let mut first_error = None;
    let mut suppressed = 0;
    loop {
        let mut s = String::new();
        match stdin.read_line(&mut s) {
            Ok(_) => {
                if s.is_empty() { break; }
                trim(&mut s);
                lines.push(s);
            },
            Err(e) => {
                if first_error.is_some() {
                    suppressed = suppressed + 1;
                } else {
                    first_error = Some(e);
                }
            }
        }
    }
    if first_error.is_some() {
        eprintln!("Warning: Failed to parse one or more lines (\"{}\"); {} additional error(s) suppressed", first_error.unwrap(), suppressed);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' && x != '\r' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

// This is technically a standard library function, but it's plastered with
// stability warnings and therefore only available on the nightly channel.
fn slice_chars(s: &str, begin: usize, end: usize) -> &str {
    assert!(begin <= end);
    let mut count = 0;
    let mut begin_byte = None;
    let mut end_byte = None;

    // This could be even more efficient by not decoding,
    // only finding the char boundaries
    for (idx, _) in s.char_indices() {
        if count == begin { begin_byte = Some(idx); }
        if count == end { end_byte = Some(idx); break; }
        count += 1;
    }
    if begin_byte.is_none() && count == begin { begin_byte = Some(s.len()) }
    if end_byte.is_none() && count == end { end_byte = Some(s.len()) }

    match (begin_byte, end_byte) {
        (None, _) => panic!("slice_chars: `begin` is beyond end of string"),
        (_, None) => panic!("slice_chars: `end` is beyond end of string"),
        (Some(a), Some(b)) => unsafe { s.get_unchecked(a..b) }
    }
}

#[cfg(test)]
mod tests {
    use super::{trim, delete_last_word};

    #[test]
    fn trim_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            trim(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("\n", "");
        should_become("\n\n", "");
        should_become("asdf", "asdf");
        should_become("asdf\n", "asdf");
        should_become("asdf\nasdf\n", "asdf\nasdf");
        should_become("asdf\r\n", "asdf");
    }

    #[test]
    fn delete_word_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            delete_last_word(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("a", "");
        should_become("asdf", "");
        should_become("asdf asdf asdf", "asdf asdf ");
        should_become("asdf asdf asdf ", "asdf asdf ");
        should_become("asdf asdf asdf  ", "asdf asdf ");
    }
}
<commit_msg>Rewrite `slice_chars` in terms of `get`<commit_after>extern crate unicode_width;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::cmp::min;
use std::collections::HashSet;
use std::env;
use std::io::{stdin, BufRead};
use std::process;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;
use unicode_width::UnicodeWidthStr;

#[cfg(windows)] pub const NEWLINE: &'static str = "\r\n";
#[cfg(not(windows))] pub const NEWLINE: &'static str = "\n";

const VERSION: &'static str = include_str!(concat!(env!("OUT_DIR"), "/version.txt"));
const TIMESTAMP: &'static str = include_str!(concat!(env!("OUT_DIR"), "/timestamp.txt"));
const TARGET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/target.txt"));
const COMMIT: &'static str = include_str!(concat!(env!("OUT_DIR"), "/commit.txt"));

fn main() {
    env::set_var("RUST_BACKTRACE", "1");
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            process::exit(1);
        },
    };

    if args.help { return; }

    if args.version {
        if COMMIT == "" {
            println!("heatseeker {} (built {} for {})", VERSION, TIMESTAMP, TARGET);
        } else {
            println!("heatseeker {}-{} (built {} for {})", VERSION, COMMIT, TIMESTAMP, TARGET);
        }
        return;
    }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search, args.filter_only);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        let desired_rows = if args.full_screen { 999 } else { 20 };
        let selections = event_loop(desired_rows, &choices, &initial_search, args.filter_only);
        print!("{}", selections);
    }
}

fn event_loop(desired_rows: u16, choices: &[&str], initial_search: &str, filter_only: bool) -> String {
    let mut search = Search::new(choices, initial_search.to_string(), filter_only);
    let mut screen = screen::new(desired_rows);

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(screen.as_mut(), &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in &keys {
            handle_key(&mut search, key, screen.visible_choices());
        }
    }

    screen.blank_screen();
    search.get_selections()
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace |
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('r') => std::panic!("This is a test backtrace"),
        Control('c') |
        Control('g') => search.cancel(),
        Control('t') => { search.toggle_selection(); search.down(visible_choices); },
        Control('p') |
        Up |
        ShiftTab => search.up(visible_choices),
        Control('n') |
        Down |
        Tab => search.down(visible_choices),
        Home => search.home(),
        End => search.end(visible_choices),
        Enter => search.done(),
        PgUp => search.pgup(visible_choices),
        PgDown => search.pgdown(visible_choices),
        _ => {},
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    scroll_offset: usize,
    cursor_index: usize,
    state: SearchState,
    selections: HashSet<String>,
    filter_only: bool,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String, filter_only: bool) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            scroll_offset: 0,
            cursor_index: 0,
            state: InProgress,
            selections: HashSet::new(),
            filter_only: filter_only,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let match_count = self.matches.len();
        let limit = min(visible_choices as usize - 1, match_count - 1);
        let should_wrap = self.scroll_offset == 0;
        if self.cursor_index == 0 {
            if should_wrap {
                if match_count > visible_choices as usize {
                    self.scroll_offset = match_count - visible_choices as usize;
                    self.cursor_index = visible_choices as usize - 1;
                } else {
                    self.cursor_index = limit;
                }
            } else {
                self.scroll_offset -= 1;
            }
        } else {
            self.cursor_index -= 1;
        }
    }

    fn down(&mut self, visible_choices: u16) {
        let match_count = self.matches.len();
        let limit = min(visible_choices as usize - 1, match_count - 1);
        let should_wrap = self.cursor_index + self.scroll_offset == match_count - 1;
        if self.cursor_index == limit {
            if should_wrap {
                self.cursor_index = 0;
                self.scroll_offset = 0;
            } else {
                self.scroll_offset += 1;
            }
        } else {
            self.cursor_index += 1;
        }
    }

    fn home(&mut self) {
        self.cursor_index = 0;
        self.scroll_offset = 0;
    }

    fn end(&mut self, visible_choices: u16) {
        self.home();
        self.up(visible_choices);
    }

    fn pgup(&mut self, visible_choices: u16) {
        for _ in 0..visible_choices {
            if self.scroll_offset == 0 && self.cursor_index == 0 {
                return;
            }
            self.up(visible_choices);
        }
    }

    fn pgdown(&mut self, visible_choices: u16) {
        for _ in 0..visible_choices {
            if self.scroll_offset + self.cursor_index == self.matches.len() - 1 {
                return;
            }
            self.down(visible_choices);
        }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.cursor_index = 0;
        self.scroll_offset = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.cursor_index = 0;
        self.scroll_offset = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.cursor_index = 0;
        self.scroll_offset = 0;
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query, self.filter_only);
            self.stale = false;
        }
    }

    fn toggle_selection(&mut self) {
        self.recompute_matches();
        let selection = self.matches.get(self.scroll_offset + self.cursor_index).unwrap_or(&"").to_string();
        if self.selections.contains(&selection) {
            self.selections.remove(&selection);
        } else {
            self.selections.insert(selection);
        }
    }

    fn get_selections(&mut self) -> String {
        let mut ret = String::new();
        if self.state != Canceled {
            for selection in &self.selections {
                ret.push_str(selection);
                ret.push_str(NEWLINE);
            }
            if ret.is_empty() {
                self.recompute_matches();
                let selection = self.matches.get(self.scroll_offset + self.cursor_index).unwrap_or(&"").to_string();
                ret.push_str(&selection);
            }
        }
        ret
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut dyn Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices){}", search.query, search.matches.len(), search.choices.len(), NEWLINE));

    print_matches(screen, &search.matches, &search.query, search.scroll_offset, search.cursor_index, &search.selections);

    let query_str: &str = &search.query;
    screen.move_cursor_to_prompt_line(2 + UnicodeWidthStr::width(query_str) as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut dyn Screen, matches: &[&str], query: &str, scroll_offset: usize, cursor_index: usize, selections: &HashSet<String>) {
    let mut i = 1;
    for choice in matches[scroll_offset..].iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width();
        let mut annotated_choice = choice.to_string();
        if selections.contains(&annotated_choice) {
            annotated_choice.push(' ');
            if cfg!(windows) {
                annotated_choice.push('√');
            } else {
                annotated_choice.push('✓');
            }
        }
        print_match(&annotated_choice, &indices, max_width, &mut |s, highlight| {
            if i == cursor_index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else if highlight {
                screen.write_red(s);
            } else { screen.write(s); }
        });
        if i >= screen.visible_choices() as usize {
            return;
        }
        screen.write(NEWLINE);
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut dyn FnMut(&str, bool)) {
    #[cfg(windows)] const MARGIN: u16 = 1;
    #[cfg(not(windows))] const MARGIN: u16 = 0;
    let max_width = max_width - MARGIN;
    let chars_in_choice = choice.chars().count();
    let mut chars_to_draw = min(chars_in_choice, max_width as usize);
    while UnicodeWidthStr::width(slice_chars(choice, 0, chars_to_draw)) > max_width as usize {
        chars_to_draw -= 1;
    }
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(slice_chars(choice, last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(slice_chars(choice, idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(slice_chars(choice, last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let stdin = stdin();
    let mut lines = Vec::new();

    let mut stdin = stdin.lock();
    let mut first_error = None;
    let mut suppressed = 0;
    loop {
        let mut s = String::new();
        match stdin.read_line(&mut s) {
            Ok(_) => {
                if s.is_empty() { break; }
                trim(&mut s);
                lines.push(s);
            },
            Err(e) => {
                if first_error.is_some() {
                    suppressed = suppressed + 1;
                } else {
                    first_error = Some(e);
                }
            }
        }
    }
    if first_error.is_some() {
        eprintln!("Warning: Failed to parse one or more lines (\"{}\"); {} additional error(s) suppressed", first_error.unwrap(), suppressed);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' && x != '\r' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

fn slice_chars(s: &str, begin: usize, end: usize) -> &str {
    s.get(begin..end).unwrap()
}

#[cfg(test)]
mod tests {
    use super::{trim, delete_last_word};

    #[test]
    fn trim_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            trim(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("\n", "");
        should_become("\n\n", "");
        should_become("asdf", "asdf");
        should_become("asdf\n", "asdf");
        should_become("asdf\nasdf\n", "asdf\nasdf");
        should_become("asdf\r\n", "asdf");
    }

    #[test]
    fn delete_word_test() {
        fn should_become(before: &str, after: &str) {
            let mut x = before.to_string();
            delete_last_word(&mut x);
            assert_eq!(after.to_string(), x);
        }
        should_become("", "");
        should_become("a", "");
        should_become("asdf", "");
        should_become("asdf asdf asdf", "asdf asdf ");
        should_become("asdf asdf asdf ", "asdf asdf ");
        should_become("asdf asdf asdf  ", "asdf asdf ");
    }
}
<|endoftext|>


<commit_before>///! GIT REQ!
mod git;
mod remotes;

use clap::{crate_authors, crate_version, App, Arg};
use log::{debug, info, trace};
use std::io::{self, Write};
use std::process;
use tabwriter::TabWriter;

/// Get the `origin` remote
fn get_origin() -> String {
    git::get_remote_url("origin")
}

/// Get the remote for the current project
fn get_remote() -> Result<Box<remotes::Remote>, String> {
    let origin = get_origin();
    remotes::get_remote(&origin)
}

/// Get the remote, fail hard otherwise
fn get_remote_hard() -> Box<remotes::Remote> {
    match get_remote() {
        Ok(x) => x,
        Err(error) => {
            eprintln!(
                "There was a problem finding the remote Git repo: {}",
                &error
            );
            process::exit(1);
        }
    }
}

/// Check out the branch corresponding to the MR ID
fn checkout_mr(mr_id: i64) {
    info!("Getting MR: {}", mr_id);
    let mut remote = get_remote_hard();
    debug!("Found remote: {}", remote);
    let branch_name = match remote.get_req_branch(mr_id) {
        Ok(name) => name,
        Err(error) => {
            eprintln!(
                "There was a problem ascertaining the branch name: {}",
                &error
            );
            process::exit(1);
        }
    };
    debug!("Got branch name: {}", branch_name);
    match git::checkout_branch(&branch_name) {
        Ok(_) => {
            info!("Done!");
        }
        Err(error) => {
            eprintln!("There was an error checking out the branch: {}", &error);
            process::exit(1)
        }
    };
}

/// Set the project ID
fn set_project_id(new_id: &str) {
    trace!("Setting project ID: {}", new_id);
    git::set_config("projectid", new_id);
    eprintln!("New project ID set!");
}

/// Print the open requests
fn list_open_requests() {
    info!("Getting open requests");
    let mut remote = get_remote_hard();
    debug!("Found remote: {}", remote);
    let mrs = remote.get_req_names().unwrap();
    let mut tw = TabWriter::new(io::stdout()).padding(4);
    for mr in &mrs {
        if remote.has_useful_branch_names() {
            writeln!(&mut tw, "{}\t{}\t{}",  mr.id, mr.source_branch, mr.title).unwrap();
        } else {
            writeln!(&mut tw, "{}\t{}",  mr.id, mr.title).unwrap();
        }
    }
    tw.flush().unwrap();
}

/// Do the thing
fn main() {
    let _ = env_logger::try_init();
    let matches = App::new("git-req")
        .bin_name("git req")
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .about(
            "Switch between merge/pull requests in your GitLab and GitHub repositories with just the request ID.",
        )
        .arg(Arg::with_name("LIST_MR")
             .long("list")
             .help("List all open requests against the repository")
             .takes_value(false)
             .required(false)
             .conflicts_with_all(&["NEW_PROJECT_ID", "REQUEST_ID"]))
        .arg(Arg::with_name("NEW_PROJECT_ID")
             .long("set-project-id")
             .value_name("PROJECT_ID")
             .help("A project ID for the current repository")
             .required(false)
             .takes_value(true)
             .conflicts_with("REQUEST_ID"))
        .arg(Arg::with_name("REQUEST_ID")
             .required(true)
             .conflicts_with_all(&["NEW_PROJECT_ID", "LIST_MR"])
             .index(1))
        .get_matches();
    if let Some(project_id) = matches.value_of("NEW_PROJECT_ID") {
        set_project_id(project_id);
    } else if matches.is_present("LIST_MR") {
        list_open_requests();
    } else {
        checkout_mr(matches.value_of("REQUEST_ID").unwrap().parse().unwrap());
    }
}
<commit_msg>Add -l as a short alias for --list.<commit_after>///! GIT REQ!
mod git;
mod remotes;

use clap::{crate_authors, crate_version, App, Arg};
use log::{debug, info, trace};
use std::io::{self, Write};
use std::process;
use tabwriter::TabWriter;

/// Get the `origin` remote
fn get_origin() -> String {
    git::get_remote_url("origin")
}

/// Get the remote for the current project
fn get_remote() -> Result<Box<remotes::Remote>, String> {
    let origin = get_origin();
    remotes::get_remote(&origin)
}

/// Get the remote, fail hard otherwise
fn get_remote_hard() -> Box<remotes::Remote> {
    match get_remote() {
        Ok(x) => x,
        Err(error) => {
            eprintln!(
                "There was a problem finding the remote Git repo: {}",
                &error
            );
            process::exit(1);
        }
    }
}

/// Check out the branch corresponding to the MR ID
fn checkout_mr(mr_id: i64) {
    info!("Getting MR: {}", mr_id);
    let mut remote = get_remote_hard();
    debug!("Found remote: {}", remote);
    let branch_name = match remote.get_req_branch(mr_id) {
        Ok(name) => name,
        Err(error) => {
            eprintln!(
                "There was a problem ascertaining the branch name: {}",
                &error
            );
            process::exit(1);
        }
    };
    debug!("Got branch name: {}", branch_name);
    match git::checkout_branch(&branch_name) {
        Ok(_) => {
            info!("Done!");
        }
        Err(error) => {
            eprintln!("There was an error checking out the branch: {}", &error);
            process::exit(1)
        }
    };
}

/// Set the project ID
fn set_project_id(new_id: &str) {
    trace!("Setting project ID: {}", new_id);
    git::set_config("projectid", new_id);
    eprintln!("New project ID set!");
}

/// Print the open requests
fn list_open_requests() {
    info!("Getting open requests");
    let mut remote = get_remote_hard();
    debug!("Found remote: {}", remote);
    let mrs = remote.get_req_names().unwrap();
    let mut tw = TabWriter::new(io::stdout()).padding(4);
    for mr in &mrs {
        if remote.has_useful_branch_names() {
            writeln!(&mut tw, "{}\t{}\t{}",  mr.id, mr.source_branch, mr.title).unwrap();
        } else {
            writeln!(&mut tw, "{}\t{}",  mr.id, mr.title).unwrap();
        }
    }
    tw.flush().unwrap();
}

/// Do the thing
fn main() {
    let _ = env_logger::try_init();
    let matches = App::new("git-req")
        .bin_name("git req")
        .author(crate_authors!("\n"))
        .version(crate_version!())
        .about(
            "Switch between merge/pull requests in your GitLab and GitHub repositories with just the request ID.",
        )
        .arg(Arg::with_name("LIST_MR")
             .long("list")
             .short("l")
             .help("List all open requests against the repository")
             .takes_value(false)
             .required(false)
             .conflicts_with_all(&["NEW_PROJECT_ID", "REQUEST_ID"]))
        .arg(Arg::with_name("NEW_PROJECT_ID")
             .long("set-project-id")
             .value_name("PROJECT_ID")
             .help("A project ID for the current repository")
             .required(false)
             .takes_value(true)
             .conflicts_with("REQUEST_ID"))
        .arg(Arg::with_name("REQUEST_ID")
             .required(true)
             .conflicts_with_all(&["NEW_PROJECT_ID", "LIST_MR"])
             .index(1))
        .get_matches();
    if let Some(project_id) = matches.value_of("NEW_PROJECT_ID") {
        set_project_id(project_id);
    } else if matches.is_present("LIST_MR") {
        list_open_requests();
    } else {
        checkout_mr(matches.value_of("REQUEST_ID").unwrap().parse().unwrap());
    }
}
<|endoftext|>