<commit_before>//! Snes9x recording format

// FIXME: It would be nice if we'd have a creation/init function that gets passed a reference to the
// emulator. That way, we could support starting a recording mid-emulation (via save states).

#![allow(dead_code, unused_variables)]  // NYI

use super::WriteSeek;
use input::Ports;
use snes::Snes;

use byteorder::{LittleEndian, WriteBytesExt};

use std::io::{self, Write, BufRead, SeekFrom};

pub struct Recorder {
    writer: Box<WriteSeek>,
    frames: u32,
}

impl super::Recorder for Recorder {
    fn new(mut writer: Box<WriteSeek>, snes: &Snes) -> io::Result<Self> {
        // Write SMV header
        try!(write!(writer, "SMV\x1A"));
        try!(writer.write_u32::<LittleEndian>(4));  // SMV Version
        try!(writer.write_u32::<LittleEndian>(0));  // uid (Unix timestamp in Snes9x)
        try!(writer.write_u32::<LittleEndian>(0));  // rerecord count (no idea what this does)
        try!(writer.write_u32::<LittleEndian>(0xdeadbeef));    // Number of frames
        // The actual number of frames is written when the recorder is dropped
        try!(writer.write_u8(0));  // controller mask (FIXME)
        try!(writer.write_u8(1));  // movie opts
        try!(writer.write_u8(0));  // sync opts
        try!(writer.write_u8(0));  // more (actual) sync opts
        try!(writer.write_u32::<LittleEndian>(0x40));  // offset to save state / cart. RAM image
        try!(writer.write_u32::<LittleEndian>(0x40 + 0x20000));  // offset to controller data

        // Write extended header (+32 Bytes)
        try!(writer.write_u32::<LittleEndian>(0xdeadbeef));  // num. of input samples

        // Controller info (for port 1 / port 2)
        // TODO

        Ok(Recorder {
            writer: writer,
            frames: 0,
        })
    }

    fn record_frame(&mut self, ports: &Ports) -> io::Result<()> {
        self.frames += 1;
        unimplemented!()
    }
}

impl Drop for Recorder {
    fn drop(&mut self) {
        // FIXME At least warn when this fails
        self.writer.seek(SeekFrom::Start(16)).ok();
        self.writer.write_u32::<LittleEndian>(self.frames).ok();
        self.writer.seek(SeekFrom::Start(32)).ok();
        // FIXME This should be the total number of input samples (might be higher when other
        // peripherals attached)
        self.writer.write_u32::<LittleEndian>(self.frames).ok();
    }
}

pub struct Replayer {
    reader: Box<BufRead>,
}

impl super::Replayer for Replayer {
    fn new(reader: Box<BufRead>, _snes: &Snes) -> io::Result<Self> {
        Ok(Replayer {
            reader: reader,
        })
    }

    fn replay_frame(&mut self, ports: &mut Ports) -> io::Result<()> {
        unimplemented!()
    }
}
<commit_msg>Write (most of) SMV header<commit_after>//! Snes9x recording format

// FIXME: It would be nice if we'd have a creation/init function that gets passed a reference to the
// emulator. That way, we could support starting a recording mid-emulation (via save states).

#![allow(dead_code, unused_variables)]  // NYI

use super::WriteSeek;
use input::{Ports, Peripheral};
use snes::Snes;

use byteorder::{LittleEndian, WriteBytesExt};

use std::io::{self, Write, BufRead, SeekFrom};

pub struct Recorder {
    writer: Box<WriteSeek>,
    frames: u32,
}

impl super::Recorder for Recorder {
    fn new(mut writer: Box<WriteSeek>, snes: &Snes) -> io::Result<Self> {
        // Write SMV header
        try!(write!(writer, "SMV\x1A"));
        try!(writer.write_u32::<LittleEndian>(4));  // SMV Version
        try!(writer.write_u32::<LittleEndian>(0));  // uid (Unix timestamp in Snes9x)
        try!(writer.write_u32::<LittleEndian>(0));  // rerecord count (no idea what this does)
        try!(writer.write_u32::<LittleEndian>(0xdeadbeef));    // Number of frames
        // The actual number of frames is written when the recorder is dropped
        try!(writer.write_u8(0));  // controller mask (FIXME)
        try!(writer.write_u8(1));  // movie opts
        try!(writer.write_u8(0));  // sync opts
        try!(writer.write_u8(0));  // more (actual) sync opts
        try!(writer.write_u32::<LittleEndian>(0x40));  // offset to save state / cart. RAM image
        try!(writer.write_u32::<LittleEndian>(0x40 + 0x20000));  // offset to controller data

        // Write extended header (+32 Bytes)
        try!(writer.write_u32::<LittleEndian>(0xdeadbeef));  // num. of input samples

        // Controller info (for port 1 / port 2)
        let input = snes.input();
        fn get_controller_type(port: &Option<Peripheral>) -> u8 {
            match *port {
                None => 0,
                Some(Peripheral::Joypad {..}) => 1,
            }
        }

        // Controller type (port 1 / port 2)
        try!(writer.write_u8(get_controller_type(&input.ports.0)));
        try!(writer.write_u8(get_controller_type(&input.ports.1)));
        // Controller ID (port 1 / port 2) - This probably has to do with the multitap (hence why
        // it's 4 bytes per port)
        // FIXME: We'll just write -1 for unplugged everywhere
        for i in 0..8 {
            try!(writer.write_i8(-1));
        }
        try!(writer.write_all(&[0; 18]));       // 18 bytes reserved for future use

        // Now follows a cartridge RAM image. It's apparently supposed to be gzip compressed and
        // should decompress into 0x20000 bytes. Since we can't gzip shit currently, we'll have to
        // waste all that space.
        try!(writer.write_all(&[0; 0x20000]));   // empty SRAM image

        Ok(Recorder {
            writer: writer,
            frames: 0,
        })
    }

    fn record_frame(&mut self, ports: &Ports) -> io::Result<()> {
        // TODO Record input data
        self.frames += 1;
        unimplemented!()
    }
}

impl Drop for Recorder {
    fn drop(&mut self) {
        info!("finalizing SMV recording ({} frames)", self.frames);

        // FIXME At least warn when this fails
        self.writer.seek(SeekFrom::Start(16)).ok();
        self.writer.write_u32::<LittleEndian>(self.frames).ok();
        self.writer.seek(SeekFrom::Start(32)).ok();
        // FIXME This should be the total number of input samples (might be higher when other
        // peripherals attached)
        self.writer.write_u32::<LittleEndian>(self.frames).ok();
    }
}

pub struct Replayer {
    reader: Box<BufRead>,
}

impl super::Replayer for Replayer {
    fn new(reader: Box<BufRead>, _snes: &Snes) -> io::Result<Self> {
        Ok(Replayer {
            reader: reader,
        })
    }

    fn replay_frame(&mut self, ports: &mut Ports) -> io::Result<()> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::ffi::OsStr;

use clap::{App, Arg, ArgMatches};
use uuid::{Uuid, UuidVersion};
use proguard;
use console::style;

use prelude::*;
use config::Config;
use utils::{validate_uuid, MachoInfo};
use commands::difutil_find::DifType;

enum DifRepr {
    Dsym(MachoInfo),
    Proguard(proguard::MappingView<'static>),
}

impl DifRepr {
    pub fn ty(&self) -> DifType {
        match self {
            &DifRepr::Dsym(..) => DifType::Dsym,
            &DifRepr::Proguard(..) => DifType::Proguard,
        }
    }

    pub fn uuids(&self) -> Vec<Uuid> {
        match self {
            &DifRepr::Dsym(ref mi) => mi.get_uuids(),
            &DifRepr::Proguard(ref pg) => vec![pg.uuid()],
        }
    }

    pub fn is_usable(&self) -> bool {
        match self {
            &DifRepr::Dsym(ref mi) => mi.has_debug_info(),
            &DifRepr::Proguard(ref pg) => pg.has_line_info(),
        }
    }

    pub fn get_problem(&self) -> Option<&str> {
        if self.is_usable() {
            None
        } else {
            Some(match self {
                &DifRepr::Dsym(ref mi) => "missing DWARF debug info",
                &DifRepr::Proguard(ref pg) => "missing line information",
            })
        }
    }
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app
        .about("given the path to a debug info file it checks it")
        .arg(Arg::with_name("type")
             .long("type")
             .short("t")
             .value_name("TYPE")
             .possible_values(&["dsym", "proguard"])
             .help("Explicitly sets the type of the debug info file."))
        .arg(Arg::with_name("path")
             .index(1)
             .required(true)
             .help("The path to the debug info file."))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, _config: &Config) -> Result<()> {
    let path = Path::new(matches.value_of("path").unwrap());

    // which types should we consider?
    let ty = matches.value_of("type").map(|t| {
        match t {
            "dsym" => DifType::Dsym,
            "proguard" => DifType::Proguard,
            _ => unreachable!()
        }
    });

    let repr = match ty {
        Some(DifType::Dsym) => DifRepr::Dsym(MachoInfo::open_path(&path)?),
        Some(DifType::Proguard) => DifRepr::Proguard(proguard::MappingView::from_path(&path)?),
        Some(_) => unreachable!(),
        None => {
            if let Ok(mi) = MachoInfo::open_path(&path) {
                DifRepr::Dsym(mi)
            } else {
                match proguard::MappingView::from_path(&path) {
                    Ok(pg) => {
                        if path.extension() == Some(OsStr::new("txt")) ||
                           pg.has_line_info() {
                            DifRepr::Proguard(pg)
                        } else {
                            fail!("invalid debug info file");
                        }
                    }
                    Err(err) => { return Err(err.into()) }
                }
            }
        }
    };

    println!("{}", style("Debug Info File Check").dim().bold());
    println!("  Type: {}", style(repr.ty()).cyan());
    println!("  Contained UUIDs:");
    for uuid in repr.uuids() {
        println!("    > {}", style(uuid).dim());
    }

    if let Some(prob) = repr.get_problem() {
        println!("  Usable: {} ({})", style("no").red(), prob);
    } else {
        println!("  Usable: {}", style("yes").green());
    }

    Ok(())
}
<commit_msg>Fixed some warnings<commit_after>use std::path::Path;
use std::ffi::OsStr;

use clap::{App, Arg, ArgMatches};
use uuid::Uuid;
use proguard;
use console::style;

use prelude::*;
use config::Config;
use utils::MachoInfo;
use commands::difutil_find::DifType;

enum DifRepr {
    Dsym(MachoInfo),
    Proguard(proguard::MappingView<'static>),
}

impl DifRepr {
    pub fn ty(&self) -> DifType {
        match self {
            &DifRepr::Dsym(..) => DifType::Dsym,
            &DifRepr::Proguard(..) => DifType::Proguard,
        }
    }

    pub fn uuids(&self) -> Vec<Uuid> {
        match self {
            &DifRepr::Dsym(ref mi) => mi.get_uuids(),
            &DifRepr::Proguard(ref pg) => vec![pg.uuid()],
        }
    }

    pub fn is_usable(&self) -> bool {
        match self {
            &DifRepr::Dsym(ref mi) => mi.has_debug_info(),
            &DifRepr::Proguard(ref pg) => pg.has_line_info(),
        }
    }

    pub fn get_problem(&self) -> Option<&str> {
        if self.is_usable() {
            None
        } else {
            Some(match self {
                &DifRepr::Dsym(..) => "missing DWARF debug info",
                &DifRepr::Proguard(..) => "missing line information",
            })
        }
    }
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app
        .about("given the path to a debug info file it checks it")
        .arg(Arg::with_name("type")
             .long("type")
             .short("t")
             .value_name("TYPE")
             .possible_values(&["dsym", "proguard"])
             .help("Explicitly sets the type of the debug info file."))
        .arg(Arg::with_name("path")
             .index(1)
             .required(true)
             .help("The path to the debug info file."))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, _config: &Config) -> Result<()> {
    let path = Path::new(matches.value_of("path").unwrap());

    // which types should we consider?
    let ty = matches.value_of("type").map(|t| {
        match t {
            "dsym" => DifType::Dsym,
            "proguard" => DifType::Proguard,
            _ => unreachable!()
        }
    });

    let repr = match ty {
        Some(DifType::Dsym) => DifRepr::Dsym(MachoInfo::open_path(&path)?),
        Some(DifType::Proguard) => DifRepr::Proguard(proguard::MappingView::from_path(&path)?),
        None => {
            if let Ok(mi) = MachoInfo::open_path(&path) {
                DifRepr::Dsym(mi)
            } else {
                match proguard::MappingView::from_path(&path) {
                    Ok(pg) => {
                        if path.extension() == Some(OsStr::new("txt")) ||
                           pg.has_line_info() {
                            DifRepr::Proguard(pg)
                        } else {
                            fail!("invalid debug info file");
                        }
                    }
                    Err(err) => { return Err(err.into()) }
                }
            }
        }
    };

    println!("{}", style("Debug Info File Check").dim().bold());
    println!("  Type: {}", style(repr.ty()).cyan());
    println!("  Contained UUIDs:");
    for uuid in repr.uuids() {
        println!("    > {}", style(uuid).dim());
    }

    if let Some(prob) = repr.get_problem() {
        println!("  Usable: {} ({})", style("no").red(), prob);
    } else {
        println!("  Usable: {}", style("yes").green());
    }

    Ok(())
}
<|endoftext|>


<commit_before>import std::{io, term};
import io::writer_util;
import syntax::codemap;
import codemap::span;

export emitter, emit;
export level, fatal, error, warning, note;
export span_handler, handler, mk_span_handler, mk_handler;
export codemap_span_handler, codemap_handler;
export ice_msg;

type emitter = fn@(cmsp: option<(codemap::codemap, span)>,
                   msg: str, lvl: level);

iface span_handler {
    fn span_fatal(sp: span, msg: str) -> !;
    fn span_err(sp: span, msg: str);
    fn span_warn(sp: span, msg: str);
    fn span_note(sp: span, msg: str);
    fn span_bug(sp: span, msg: str) -> !;
    fn span_unimpl(sp: span, msg: str) -> !;
    fn handler() -> handler;
}

iface handler {
    fn fatal(msg: str) -> !;
    fn err(msg: str);
    fn bump_err_count();
    fn has_errors() -> bool;
    fn abort_if_errors();
    fn warn(msg: str);
    fn note(msg: str);
    fn bug(msg: str) -> !;
    fn unimpl(msg: str) -> !;
    fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level);
}

type handler_t = @{
    mutable err_count: uint,
    _emit: emitter
};

type codemap_t = @{
    handler: handler,
    cm: codemap::codemap
};

impl codemap_span_handler of span_handler for codemap_t {
    fn span_fatal(sp: span, msg: str) -> ! {
        self.handler.emit(some((self.cm, sp)), msg, fatal);
        fail;
    }
    fn span_err(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    fn span_warn(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, warning);
    }
    fn span_note(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, note);
    }
    fn span_bug(sp: span, msg: str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    fn span_unimpl(sp: span, msg: str) -> ! {
        self.span_bug(sp, "unimplemented " + msg);
    }
    fn handler() -> handler {
        self.handler
    }
}

impl codemap_handler of handler for handler_t {
    fn fatal(msg: str) -> ! {
        self._emit(none, msg, fatal);
        fail;
    }
    fn err(msg: str) {
        self._emit(none, msg, error);
    }
    fn bump_err_count() {
        self.err_count += 1u;
    }
    fn has_errors() -> bool { self.err_count > 0u }
    fn abort_if_errors() {
        if self.err_count > 0u {
            self.fatal("aborting due to previous errors");
        }
    }
    fn warn(msg: str) {
        self._emit(none, msg, warning);
    }
    fn note(msg: str) {
        self._emit(none, msg, note);
    }
    fn bug(msg: str) -> ! {
        self.fatal(ice_msg(msg));
    }
    fn unimpl(msg: str) -> ! { self.bug("unimplemented " + msg); }
    fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level) {
        self._emit(cmsp, msg, lvl);
    }
}

fn ice_msg(msg: str) -> str {
    #fmt["internal compiler error %s", msg]
}

fn mk_span_handler(handler: handler, cm: codemap::codemap) -> span_handler {
    @{ handler: handler, cm: cm } as span_handler
}

fn mk_handler(emitter: option<emitter>) -> handler {

    let emit = alt emitter {
      some(e) { e }
      none {
        let f = fn@(cmsp: option<(codemap::codemap, span)>,
            msg: str, t: level) {
            emit(cmsp, msg, t);
        };
        f
      }
    };

    @{
        mutable err_count: 0u,
        _emit: emit
    } as handler
}

enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> str {
    alt lvl {
      fatal { "error" }
      error { "error" }
      warning { "warning" }
      note { "note" }
    }
}

fn diagnosticcolor(lvl: level) -> u8 {
    alt lvl {
      fatal { term::color_bright_red }
      error { term::color_bright_red }
      warning { term::color_bright_yellow }
      note { term::color_bright_green }
    }
}

fn print_diagnostic(topic: str, lvl: level, msg: str) {
    if str::is_not_empty(topic) {
        io::stdout().write_str(#fmt["%s ", topic]);
    }
    if term::color_supported() {
        term::fg(io::stdout(), diagnosticcolor(lvl));
    }
    io::stdout().write_str(#fmt["%s:", diagnosticstr(lvl)]);
    if term::color_supported() {
        term::reset(io::stdout());
    }
    io::stdout().write_str(#fmt[" %s\n", msg]);
}

fn emit(cmsp: option<(codemap::codemap, span)>,
        msg: str, lvl: level) {
    alt cmsp {
      some((cm, sp)) {
        let ss = codemap::span_to_str(sp, cm);
        let lines = codemap::span_to_lines(sp, cm);
        print_diagnostic(ss, lvl, msg);
        highlight_lines(cm, sp, lines);
      }
      none {
        print_diagnostic("", lvl, msg);
      }
    }
}

fn highlight_lines(cm: codemap::codemap, sp: span,
                   lines: @codemap::file_lines) {

    // If we're not looking at a real file then we can't re-open it to
    // pull out the lines
    if lines.name == "-" { ret; }

    // FIXME: reading in the entire file is the worst possible way to
    //        get access to the necessary lines.
    let file = alt io::read_whole_file_str(lines.name) {
      result::ok(file) { file }
      result::err(e) {
        // Hard to report errors while reporting an error
        ret;
      }
    };
    let fm = codemap::get_filemap(cm, lines.name);

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let elided = false;
    let display_lines = lines.lines;
    if vec::len(display_lines) > max_lines {
        display_lines = vec::slice(display_lines, 0u, max_lines);
        elided = true;
    }
    // Print the offending lines
    for line: uint in display_lines {
        io::stdout().write_str(#fmt["%s:%u ", fm.name, line + 1u]);
        let s = codemap::get_line(fm, line as int, file);
        if !str::ends_with(s, "\n") { s += "\n"; }
        io::stdout().write_str(s);
    }
    if elided {
        let last_line = display_lines[vec::len(display_lines) - 1u];
        let s = #fmt["%s:%u ", fm.name, last_line + 1u];
        let indent = str::char_len(s);
        let out = "";
        while indent > 0u { out += " "; indent -= 1u; }
        out += "...\n";
        io::stdout().write_str(out);
    }

    // If there's one line at fault we can easily point to the problem
    if vec::len(lines.lines) == 1u {
        let lo = codemap::lookup_char_pos(cm, sp.lo);
        let digits = 0u;
        let num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = str::char_len(fm.name) + digits + lo.col + 3u;
        let s = "";
        while left > 0u { str::push_char(s, ' '); left -= 1u; }

        s += "^";
        let hi = codemap::lookup_char_pos(cm, sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let width = hi.col - lo.col - 1u;
            while width > 0u { str::push_char(s, '~'); width -= 1u; }
        }
        io::stdout().write_str(s + "\n");
    }
}
<commit_msg>rustc: Bump the error count on non-span errors<commit_after>import std::{io, term};
import io::writer_util;
import syntax::codemap;
import codemap::span;

export emitter, emit;
export level, fatal, error, warning, note;
export span_handler, handler, mk_span_handler, mk_handler;
export codemap_span_handler, codemap_handler;
export ice_msg;

type emitter = fn@(cmsp: option<(codemap::codemap, span)>,
                   msg: str, lvl: level);

iface span_handler {
    fn span_fatal(sp: span, msg: str) -> !;
    fn span_err(sp: span, msg: str);
    fn span_warn(sp: span, msg: str);
    fn span_note(sp: span, msg: str);
    fn span_bug(sp: span, msg: str) -> !;
    fn span_unimpl(sp: span, msg: str) -> !;
    fn handler() -> handler;
}

iface handler {
    fn fatal(msg: str) -> !;
    fn err(msg: str);
    fn bump_err_count();
    fn has_errors() -> bool;
    fn abort_if_errors();
    fn warn(msg: str);
    fn note(msg: str);
    fn bug(msg: str) -> !;
    fn unimpl(msg: str) -> !;
    fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level);
}

type handler_t = @{
    mutable err_count: uint,
    _emit: emitter
};

type codemap_t = @{
    handler: handler,
    cm: codemap::codemap
};

impl codemap_span_handler of span_handler for codemap_t {
    fn span_fatal(sp: span, msg: str) -> ! {
        self.handler.emit(some((self.cm, sp)), msg, fatal);
        fail;
    }
    fn span_err(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, error);
        self.handler.bump_err_count();
    }
    fn span_warn(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, warning);
    }
    fn span_note(sp: span, msg: str) {
        self.handler.emit(some((self.cm, sp)), msg, note);
    }
    fn span_bug(sp: span, msg: str) -> ! {
        self.span_fatal(sp, ice_msg(msg));
    }
    fn span_unimpl(sp: span, msg: str) -> ! {
        self.span_bug(sp, "unimplemented " + msg);
    }
    fn handler() -> handler {
        self.handler
    }
}

impl codemap_handler of handler for handler_t {
    fn fatal(msg: str) -> ! {
        self._emit(none, msg, fatal);
        fail;
    }
    fn err(msg: str) {
        self._emit(none, msg, error);
        self.bump_err_count();
    }
    fn bump_err_count() {
        self.err_count += 1u;
    }
    fn has_errors() -> bool { self.err_count > 0u }
    fn abort_if_errors() {
        if self.err_count > 0u {
            self.fatal("aborting due to previous errors");
        }
    }
    fn warn(msg: str) {
        self._emit(none, msg, warning);
    }
    fn note(msg: str) {
        self._emit(none, msg, note);
    }
    fn bug(msg: str) -> ! {
        self.fatal(ice_msg(msg));
    }
    fn unimpl(msg: str) -> ! { self.bug("unimplemented " + msg); }
    fn emit(cmsp: option<(codemap::codemap, span)>, msg: str, lvl: level) {
        self._emit(cmsp, msg, lvl);
    }
}

fn ice_msg(msg: str) -> str {
    #fmt["internal compiler error %s", msg]
}

fn mk_span_handler(handler: handler, cm: codemap::codemap) -> span_handler {
    @{ handler: handler, cm: cm } as span_handler
}

fn mk_handler(emitter: option<emitter>) -> handler {

    let emit = alt emitter {
      some(e) { e }
      none {
        let f = fn@(cmsp: option<(codemap::codemap, span)>,
            msg: str, t: level) {
            emit(cmsp, msg, t);
        };
        f
      }
    };

    @{
        mutable err_count: 0u,
        _emit: emit
    } as handler
}

enum level {
    fatal,
    error,
    warning,
    note,
}

fn diagnosticstr(lvl: level) -> str {
    alt lvl {
      fatal { "error" }
      error { "error" }
      warning { "warning" }
      note { "note" }
    }
}

fn diagnosticcolor(lvl: level) -> u8 {
    alt lvl {
      fatal { term::color_bright_red }
      error { term::color_bright_red }
      warning { term::color_bright_yellow }
      note { term::color_bright_green }
    }
}

fn print_diagnostic(topic: str, lvl: level, msg: str) {
    if str::is_not_empty(topic) {
        io::stdout().write_str(#fmt["%s ", topic]);
    }
    if term::color_supported() {
        term::fg(io::stdout(), diagnosticcolor(lvl));
    }
    io::stdout().write_str(#fmt["%s:", diagnosticstr(lvl)]);
    if term::color_supported() {
        term::reset(io::stdout());
    }
    io::stdout().write_str(#fmt[" %s\n", msg]);
}

fn emit(cmsp: option<(codemap::codemap, span)>,
        msg: str, lvl: level) {
    alt cmsp {
      some((cm, sp)) {
        let ss = codemap::span_to_str(sp, cm);
        let lines = codemap::span_to_lines(sp, cm);
        print_diagnostic(ss, lvl, msg);
        highlight_lines(cm, sp, lines);
      }
      none {
        print_diagnostic("", lvl, msg);
      }
    }
}

fn highlight_lines(cm: codemap::codemap, sp: span,
                   lines: @codemap::file_lines) {

    // If we're not looking at a real file then we can't re-open it to
    // pull out the lines
    if lines.name == "-" { ret; }

    // FIXME: reading in the entire file is the worst possible way to
    //        get access to the necessary lines.
    let file = alt io::read_whole_file_str(lines.name) {
      result::ok(file) { file }
      result::err(e) {
        // Hard to report errors while reporting an error
        ret;
      }
    };
    let fm = codemap::get_filemap(cm, lines.name);

    // arbitrarily only print up to six lines of the error
    let max_lines = 6u;
    let elided = false;
    let display_lines = lines.lines;
    if vec::len(display_lines) > max_lines {
        display_lines = vec::slice(display_lines, 0u, max_lines);
        elided = true;
    }
    // Print the offending lines
    for line: uint in display_lines {
        io::stdout().write_str(#fmt["%s:%u ", fm.name, line + 1u]);
        let s = codemap::get_line(fm, line as int, file);
        if !str::ends_with(s, "\n") { s += "\n"; }
        io::stdout().write_str(s);
    }
    if elided {
        let last_line = display_lines[vec::len(display_lines) - 1u];
        let s = #fmt["%s:%u ", fm.name, last_line + 1u];
        let indent = str::char_len(s);
        let out = "";
        while indent > 0u { out += " "; indent -= 1u; }
        out += "...\n";
        io::stdout().write_str(out);
    }

    // If there's one line at fault we can easily point to the problem
    if vec::len(lines.lines) == 1u {
        let lo = codemap::lookup_char_pos(cm, sp.lo);
        let digits = 0u;
        let num = (lines.lines[0] + 1u) / 10u;

        // how many digits must be indent past?
        while num > 0u { num /= 10u; digits += 1u; }

        // indent past |name:## | and the 0-offset column location
        let left = str::char_len(fm.name) + digits + lo.col + 3u;
        let s = "";
        while left > 0u { str::push_char(s, ' '); left -= 1u; }

        s += "^";
        let hi = codemap::lookup_char_pos(cm, sp.hi);
        if hi.col != lo.col {
            // the ^ already takes up one space
            let width = hi.col - lo.col - 1u;
            while width > 0u { str::push_char(s, '~'); width -= 1u; }
        }
        io::stdout().write_str(s + "\n");
    }
}
<|endoftext|>


<commit_before>struct NFA
{
	states: ~[State],
}

impl NFA
{
	pub fn new()	->	NFA
	{
		NFA { states: ~[] }
	}

	pub fn add_state(&mut self, state: State)
	{
		self.states.push(state);
	}

	pub fn add_transition(&mut self, id0: int, c: char, id1: int)
	{
		self.states[id0].add_transition(c, id1);
	}

	pub fn print(&self)
	{
		println("\nprinting states:");
		for i in range(0, self.states.len())
		{
			print(fmt!("%u: ", i));
			self.states[i].print();
		}
		println("");
	}

	pub fn pop(&mut self)	->	State
	{
		self.states.pop()
	}

	pub fn len(&self)	->	int
	{
		self.states.len() as int
	}
	pub fn check(&self, string: ~str, c: ~[int])		->	bool
	{
		let mut curr = c.clone();
		//println(fmt!("check: string=%? c=%?", string, c));
		if string.len() > 0
		{
			let c = string.char_at(0);
			let mut next = ~[];
			// look to see if any current states have c or an
			// epsilon transition as a value
			let mut l = curr.len();
			let mut i = 0;
			while i < l
			{
				for j in range(0, self.states[curr[i]].transitions.len())
				{
					let (t, id) = (self.states[curr[i]].transitions[j]).clone();
					if t == c
					{
						next.push(id);
					}
					else if t == '%'
					{
						curr.push(id);
						l += 1;
					}
				}
				i += 1;
			}
			if next.len() == 0 { return false; }
			return self.check(string.slice_from(1).to_owned(), next);
		}
		else // the NFA has eaten all of the characters
		{
			// see if we're in a matching state
			for i in range(0, curr.len())
			{
				if self.states[curr[i]].accept
				{
					return true;
				}
			}
			// check to see if there are any epsilon transitions we
			// can make
			let mut next = ~[];
			for i in range(0, curr.len())
			{
				for j in range(0, self.states[curr[i]].transitions.len())
				{
					let (t , id) = (self.states[curr[i]].transitions[j]).clone();
					if t == '%'
					{
						next.push(id);
					}
				}
			}
			//println(fmt!("curr=%? next=%?", curr, next));
			if next.len() > 0
			{
				return self.check(string, next);
			}
			return false;
		}
	}
}

#[deriving(Clone, Eq)]
struct State
{
	accept: bool,
	transitions: ~[(char, int)]
}

impl State
{
	pub fn new(b: bool)	->	State
	{
		State { accept: b, transitions: ~[] }
	}

	pub fn print(&self)
	{
		print(fmt!("matching=%? transitions={", self.accept));
		for i in range(0, self.transitions.len())
		{
			print(fmt!("%?,", self.transitions[i]));
		}
		println("}");
	}

	pub fn is_accept(&mut self, b: bool)
	{
		self.accept = b;
	}

	pub fn add_transition(&mut self, c: char, id: int )
	{
		self.transitions.push((c, id));
	}
}

fn main()
{
	let test = std::os::args()[1];
	let lang = ['a', 'b'];
	let strings = ~[~"", ~"a", ~"b", ~"aa", ~"bb", ~"ab", ~"ba", ~"aab", ~"aba", ~"baa", ~"abb", ~"bab", ~"bba", ~"bbb", ~"aaa", ~"abbb", ~"abba"];
	let mut nfa = NFA::new();
	//let mut parenthesis: ~[int] = ~[];
	//let head_state = State::new(false);
	//nfa.add_state(head_state);
	let mut j = -1;
	let mut escaped = false;
	for i in range(1,(test.len()+1) as int)
	{
		let c = test.char_at((i-1) as uint);
		if c == '\\'
		{
			escaped = true;
		}
		/*
		else if c == '(' && !escaped
		{
			// note the place and make an 'in' state
			parenthesis.push(i);
			let state_in = State::new('%', false);
			nfa.add_state(state_in);
			j += 1;

			// link this and the previous
			nfa.link_states(j-1, j);
		}
		else if c == ')' && !escaped
		{
			let state_out = State::new('%', false);
			nfa.add_state(state_out);
			j += 1;

			// link to state_in
			nfa.link_states(j, parenthesis.pop()-1);

			// link to prev
			nfa.link_states(j-1, j);
		}
		*/
		else if c == '*' && !escaped
		{
			// link curr to itself
			for i in range(0, nfa.states[j].transitions.len())
			{
				let (a,_) = (nfa.states[j].transitions[i]).clone();
				if a != '%'
				{
					nfa.add_transition(j, a, j);
				}
			}
			// add epsilon transition to skip this state
			nfa.add_transition(j, '%', j+1);

		}
		else if c == '+' && !escaped
		{
			let state_next = State::new(false);
			// link next to curr

			nfa.add_state(state_next);
			j += 1;

			// link next to curr
			nfa.add_transition(j, '%', j-1);
			// add transition to next state
			nfa.add_transition(j, '%', j+1);
		}
		/*
		else if c == '?' && !escaped
		{
			let state_out = State::new('%', false);

			nfa.add_state(state_out);
			j += 1;

			// link to curr to out
			nfa.link_states(j-1, j);

			// link prev to out
			nfa.link_states(j-2, j);
		}
		*/
		else if c == '.' && !escaped
		{
			let state = State::new(false);
			nfa.add_state(state);
			j += 1;

			// unconditional jump
			for i in range(0, lang.len())
			{
				nfa.add_transition(j, lang[i], j+1);
			}
		}
		else
		{
			let state = State::new(false);
			nfa.add_state(state);
			j += 1;

			// link with next
			nfa.add_transition(j, c, j+1);
			escaped = false;
		}

		nfa.print();
	}
	// make the last state true
	let state = State::new(true);
	nfa.add_state(state);

	nfa.print();

	let curr = ~[0];
	println(fmt!("Regex is: %s", test));
	for i in range(0, strings.len())
	{
		println(fmt!("%? %?", strings[i], nfa.check(strings[i].clone(), curr.clone())));
	}

}
<commit_msg>simplified .<commit_after>struct NFA
{
	states: ~[State],
}

impl NFA
{
	pub fn new()	->	NFA
	{
		NFA { states: ~[] }
	}

	pub fn add_state(&mut self, state: State)
	{
		self.states.push(state);
	}

	pub fn add_transition(&mut self, id0: int, c: char, id1: int)
	{
		self.states[id0].add_transition(c, id1);
	}

	pub fn print(&self)
	{
		println("\nprinting states:");
		for i in range(0, self.states.len())
		{
			print(fmt!("%u: ", i));
			self.states[i].print();
		}
		println("");
	}

	pub fn pop(&mut self)	->	State
	{
		self.states.pop()
	}

	pub fn len(&self)	->	int
	{
		self.states.len() as int
	}
	pub fn check(&self, string: ~str, c: ~[int])		->	bool
	{
		let mut curr = c.clone();
		//println(fmt!("check: string=%? c=%?", string, c));
		if string.len() > 0
		{
			let c = string.char_at(0);
			let mut next = ~[];
			// look to see if any current states have c or an
			// epsilon transition as a value
			let mut l = curr.len();
			let mut i = 0;
			while i < l
			{
				for j in range(0, self.states[curr[i]].transitions.len())
				{
					let (t, id) = (self.states[curr[i]].transitions[j]).clone();
					if t == c
					{
						next.push(id);
					}
					else if t == '%'
					{
						curr.push(id);
						l += 1;
					}
				}
				i += 1;
			}
			if next.len() == 0 { return false; }
			return self.check(string.slice_from(1).to_owned(), next);
		}
		else // the NFA has eaten all of the characters
		{
			// see if we're in a matching state
			for i in range(0, curr.len())
			{
				if self.states[curr[i]].accept
				{
					return true;
				}
			}
			// check to see if there are any epsilon transitions we
			// can make
			let mut next = ~[];
			for i in range(0, curr.len())
			{
				for j in range(0, self.states[curr[i]].transitions.len())
				{
					let (t , id) = (self.states[curr[i]].transitions[j]).clone();
					if t == '%'
					{
						next.push(id);
					}
				}
			}
			//println(fmt!("curr=%? next=%?", curr, next));
			if next.len() > 0
			{
				return self.check(string, next);
			}
			return false;
		}
	}
}

#[deriving(Clone, Eq)]
struct State
{
	accept: bool,
	transitions: ~[(char, int)]
}

impl State
{
	pub fn new(b: bool)	->	State
	{
		State { accept: b, transitions: ~[] }
	}

	pub fn print(&self)
	{
		print(fmt!("matching=%? transitions={", self.accept));
		for i in range(0, self.transitions.len())
		{
			print(fmt!("%?,", self.transitions[i]));
		}
		println("}");
	}

	pub fn is_accept(&mut self, b: bool)
	{
		self.accept = b;
	}

	pub fn add_transition(&mut self, c: char, id: int )
	{
		self.transitions.push((c, id));
	}
}

fn main()
{
	let test = std::os::args()[1];
	let lang = ['a', 'b'];
	let strings = ~[~"", ~"a", ~"b", ~"aa", ~"bb", ~"ab", ~"ba", ~"aab", ~"aba", ~"baa", ~"abb", ~"bab", ~"bba", ~"bbb", ~"aaa", ~"abbb", ~"abba"];
	let mut nfa = NFA::new();
	//let mut parenthesis: ~[int] = ~[];
	//let head_state = State::new(false);
	//nfa.add_state(head_state);
	let mut j = -1;
	let mut escaped = false;
	for i in range(1,(test.len()+1) as int)
	{
		let c = test.char_at((i-1) as uint);
		if c == '\\'
		{
			escaped = true;
		}
		/*
		else if c == '(' && !escaped
		{
			// note the place and make an 'in' state
			parenthesis.push(i);
			let state_in = State::new('%', false);
			nfa.add_state(state_in);
			j += 1;

			// link this and the previous
			nfa.link_states(j-1, j);
		}
		else if c == ')' && !escaped
		{
			let state_out = State::new('%', false);
			nfa.add_state(state_out);
			j += 1;

			// link to state_in
			nfa.link_states(j, parenthesis.pop()-1);

			// link to prev
			nfa.link_states(j-1, j);
		}
		*/
		else if c == '*' && !escaped
		{
			// link curr to itself
			for i in range(0, nfa.states[j].transitions.len())
			{
				let (a,_) = (nfa.states[j].transitions[i]).clone();
				if a != '%'
				{
					nfa.add_transition(j, a, j);
				}
			}
			// add epsilon transition to skip this state
			nfa.add_transition(j, '%', j+1);

		}
		else if c == '+' && !escaped
		{
			let state_next = State::new(false);
			// link next to curr

			nfa.add_state(state_next);
			j += 1;

			// link next to curr
			nfa.add_transition(j, '%', j-1);
			// add transition to next state
			nfa.add_transition(j, '%', j+1);
		}
		else if c == '?' && !escaped
		{
			// add epsilon transition
			nfa.add_transition(j, '%', j+1);
		}
		else if c == '.' && !escaped
		{
			let state = State::new(false);
			nfa.add_state(state);
			j += 1;

			// unconditional jump
			for i in range(0, lang.len())
			{
				nfa.add_transition(j, lang[i], j+1);
			}
		}
		else
		{
			let state = State::new(false);
			nfa.add_state(state);
			j += 1;

			// link with next
			nfa.add_transition(j, c, j+1);
			escaped = false;
		}

		nfa.print();
	}
	// make the last state true
	let state = State::new(true);
	nfa.add_state(state);

	nfa.print();

	let curr = ~[0];
	println(fmt!("Regex is: %s", test));
	for i in range(0, strings.len())
	{
		println(fmt!("%? %?", strings[i], nfa.check(strings[i].clone(), curr.clone())));
	}

}
<|endoftext|>


<commit_before>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use tetromino::*;
use settings::*;
use stats::GameStats;

#[derive(Debug, PartialEq)]
pub enum GameState {
    Playing,
    Paused,
    GameOver
}

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            state: GameState::Playing
        }
    }

    pub fn reset(&mut self) {
        self.board = Board::new();
        let mut randomizer = Randomizer::new();
        self.current_piece = randomizer.create_piece();
        self.next_piece = randomizer.create_piece();
        self.randomizer = randomizer;
        self.stats = GameStats::new();
        self.time_since_moved = 0.0;
        self.set_game_state(GameState::Playing);
    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    pub fn is_playing(&self) -> bool {
        self.state == GameState::Playing
    }

    pub fn is_paused(&self) -> bool {
        self.state == GameState::Paused
    }

    pub fn is_game_over(&self) -> bool {
        self.state == GameState::GameOver
    }

    pub fn set_game_state(&mut self, state: GameState) {
        self.state = state;
    }

    fn drop_delay(&self) -> f64 {
        ((MAX_GAME_LEVEL + 1) - self.stats.get_level()) as f64 * 0.10
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
        self.remove_completed_lines();
        self.get_new_piece();
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        while self.is_valid_board_position(&ghost) {
            ghost.y += 1;
        }
        ghost.y -= 1;
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        if self.is_valid_board_position(&next){
            self.set_current_piece(next);
            self.next_piece = self.randomizer.create_piece();
        } else {
            self.state = GameState::GameOver;
        }
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if !self.is_valid_board_position(&moved) {
            self.lock_current_piece();
        } else {
            self.set_current_piece(moved);
        }
    }

    fn handle_playing_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                    }
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Paused
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn handle_paused_input(&mut self, input: Input) {
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Playing
                    },
                    _ => {}
                }
            },
            _ => {}
        }
    }

    pub fn on_input(&mut self, input: Input) {
        match self.state {
            GameState::Playing => self.handle_playing_input(input),
            GameState::Paused => self.handle_paused_input(input),
            _ => {}
        }
    }

    pub fn on_update(&mut self, update_args: UpdateArgs) {
        match self.state {
            GameState::Playing => {
                self.time_since_moved += update_args.dt;
                let delay = self.drop_delay();
                if self.time_since_moved >= delay {
                    self.time_since_moved -= delay;
                    self.update();
                }
            },
            _ => {}
        }
    }

    pub fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_in_grid(RenderType::Ghost, context, graphics);
        self.current_piece.render_in_grid(RenderType::Normal, context, graphics);
        self.next_piece.render_in_next_piece(RenderType::Normal, context, graphics);
    }
}

#[derive(Clone, Copy)]
pub struct ScreenPosition {
    pub x: f64,
    pub y: f64
}
impl ScreenPosition {
    pub fn new(x: f64, y: f64) -> ScreenPosition {
        ScreenPosition {x: x, y: y}
    }
}
<commit_msg>move the gamestate enum to the end of the file<commit_after>use piston_window::*;

use board::Board;
use randomizer::Randomizer;
use tetromino::*;
use settings::*;
use stats::GameStats;

pub struct Rustris {
    board: Board,
    randomizer: Randomizer,
    current_piece: Piece,
    next_piece: Piece,
    stats: GameStats,
    time_since_moved: f64,
    state: GameState
}
impl Rustris {
    pub fn new() -> Rustris {
        let mut randomizer = Randomizer::new();
        let current_piece = randomizer.create_piece();
        let next_piece = randomizer.create_piece();
        Rustris {
            board: Board::new(),
            randomizer: randomizer,
            current_piece: current_piece,
            next_piece: next_piece,
            stats: GameStats::new(),
            time_since_moved: 0.0,
            state: GameState::Playing
        }
    }

    pub fn reset(&mut self) {
        self.board = Board::new();
        let mut randomizer = Randomizer::new();
        self.current_piece = randomizer.create_piece();
        self.next_piece = randomizer.create_piece();
        self.randomizer = randomizer;
        self.stats = GameStats::new();
        self.time_since_moved = 0.0;
        self.set_game_state(GameState::Playing);
    }

    pub fn get_game_stats(&self) -> &GameStats {
        &self.stats
    }

    pub fn set_current_piece(&mut self, piece: Piece) {
        self.current_piece = piece;
    }

    pub fn is_playing(&self) -> bool {
        self.state == GameState::Playing
    }

    pub fn is_paused(&self) -> bool {
        self.state == GameState::Paused
    }

    pub fn is_game_over(&self) -> bool {
        self.state == GameState::GameOver
    }

    pub fn set_game_state(&mut self, state: GameState) {
        self.state = state;
    }

    fn drop_delay(&self) -> f64 {
        ((MAX_GAME_LEVEL + 1) - self.stats.get_level()) as f64 * 0.10
    }

    fn is_valid_board_position(&self, piece: &Piece) -> bool {
        // TODO: Add tests for this
        piece.blocks_iter().all(|block| {
            (block.x >= 0 && block.x < WIDTH_IN_BLOCKS &&
             block.y >= 0 && block.y < HEIGHT_IN_BLOCKS &&
             !self.board.is_space_occupied(block))
        })
    }

    fn lock_current_piece(&mut self) {
        self.board.set_piece(&self.current_piece);
        self.remove_completed_lines();
        self.get_new_piece();
    }

    fn remove_completed_lines(&mut self) {
        let number_removed = self.board.remove_completed_rows();
        self.stats.score_completed_lines(number_removed);
    }

    fn calculate_ghost_piece(&self) -> Piece {
        let mut ghost = self.current_piece;
        while self.is_valid_board_position(&ghost) {
            ghost.y += 1;
        }
        ghost.y -= 1;
        ghost
    }

    fn get_new_piece(&mut self) {
        let next = self.next_piece;
        if self.is_valid_board_position(&next){
            self.set_current_piece(next);
            self.next_piece = self.randomizer.create_piece();
        } else {
            self.state = GameState::GameOver;
        }
    }

    fn update(&mut self) {
        let moved = self.current_piece.moved(Direction::Down);
        if !self.is_valid_board_position(&moved) {
            self.lock_current_piece();
        } else {
            self.set_current_piece(moved);
        }
    }

    fn handle_playing_input(&mut self, input: Input) {
        let mut moved: Option<Piece> = None;
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::Up) => {
                        moved = Some(self.current_piece.rotated());
                    }
                    Button::Keyboard(Key::Down) => {
                        let move_down = self.current_piece.moved(Direction::Down);
                        if self.is_valid_board_position(&move_down) {
                            self.time_since_moved = 0.0;
                            self.stats.score_soft_drop();
                            self.set_current_piece(move_down);
                        }
                    }
                    Button::Keyboard(Key::Left) => {
                        moved = Some(self.current_piece.moved(Direction::Left));
                    }
                    Button::Keyboard(Key::Right) => {
                        moved = Some(self.current_piece.moved(Direction::Right));
                    }
                    Button::Keyboard(Key::Space) => {
                        let ghost = self.calculate_ghost_piece();
                        let rows_dropped = (ghost.y - self.current_piece.y) as u32;
                        self.stats.score_hard_drop(rows_dropped);
                        self.set_current_piece(ghost);
                        self.lock_current_piece();
                    }
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Paused
                    }
                    _ => {}
                }
            }
            _ => {}
        }
        if let Some(piece) = moved {
            if self.is_valid_board_position(&piece) {
                self.set_current_piece(piece);
            }
        }
    }

    fn handle_paused_input(&mut self, input: Input) {
        match input {
            Input::Press(key) => {
                match key {
                    Button::Keyboard(Key::P) => {
                        self.state = GameState::Playing
                    },
                    _ => {}
                }
            },
            _ => {}
        }
    }

    pub fn on_input(&mut self, input: Input) {
        match self.state {
            GameState::Playing => self.handle_playing_input(input),
            GameState::Paused => self.handle_paused_input(input),
            _ => {}
        }
    }

    pub fn on_update(&mut self, update_args: UpdateArgs) {
        match self.state {
            GameState::Playing => {
                self.time_since_moved += update_args.dt;
                let delay = self.drop_delay();
                if self.time_since_moved >= delay {
                    self.time_since_moved -= delay;
                    self.update();
                }
            },
            _ => {}
        }
    }

    pub fn render(&mut self, context: Context, graphics: &mut G2d) {
        self.board.render(context, graphics);
        self.calculate_ghost_piece().render_in_grid(RenderType::Ghost, context, graphics);
        self.current_piece.render_in_grid(RenderType::Normal, context, graphics);
        self.next_piece.render_in_next_piece(RenderType::Normal, context, graphics);
    }
}

#[derive(Debug, PartialEq)]
pub enum GameState {
    Playing,
    Paused,
    GameOver
}

#[derive(Clone, Copy)]
pub struct ScreenPosition {
    pub x: f64,
    pub y: f64
}
impl ScreenPosition {
    pub fn new(x: f64, y: f64) -> ScreenPosition {
        ScreenPosition {x: x, y: y}
    }
}
<|endoftext|>


<commit_before>
use wtest_basic::*;

#[cfg( feature = "in_wtools" )]
use wtools::string as TheModule;
#[cfg( not( feature = "in_wtools" ) )]
use wstring_tools as TheModule;

// use TheModule::prelude::*;

//

fn _basic()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

fn _basic_form_and_methods()
{
  let src = "abc";
  let opts = TheModule::string::split()
  .src( src )
  .form();
  let iter = opts.split();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );

  let src = "abc";
  let opts = TheModule::string::split()
  .src( src )
  .form();
  let iter = opts.split_fast();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

//

fn _split_with_option_preserving_empty()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_preserving_delimeters()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_stripping()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .preserving_delimeters( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "c" ] );
}

//

fn _basic_split_with_vector()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "abc", ] );

  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![ "a", "b", "" ] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );

  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![ "b", "d" ] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

test_suite!
{
  basic,
  basic_form_and_methods,
  split_with_option_preserving_empty,
  split_with_option_preserving_delimeters,
  split_with_option_stripping,
  basic_split_with_vector,
}
<commit_msg>Write test routine `split_with_option_quoting`<commit_after>
use wtest_basic::*;

#[cfg( feature = "in_wtools" )]
use wtools::string as TheModule;
#[cfg( not( feature = "in_wtools" ) )]
use wstring_tools as TheModule;

// use TheModule::prelude::*;

//

fn _basic()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

fn _basic_form_and_methods()
{
  let src = "abc";
  let opts = TheModule::string::split()
  .src( src )
  .form();
  let iter = opts.split();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );

  let src = "abc";
  let opts = TheModule::string::split()
  .src( src )
  .form();
  let iter = opts.split_fast();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

//

fn _split_with_option_preserving_empty()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_preserving_delimeters()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_stripping()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .preserving_delimeters( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "c" ] );
}

//

fn _split_with_option_quoting()
{
  let src = "a b c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c", " ", "d" ] );

  let src = "a 'b' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( true )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "'b'", " ", "c", " ", "d" ] );

  let src = "a 'b ' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( true )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "'b '", " ", "c", " ", "d" ] );

  let src = "a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( true )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "'b '", "c", " ", "d" ] );

  let src = "'a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( true )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "'a '", "b", " ", "'c d" ] );

  /* */

  let src = "a b c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( false )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c", "d" ] );

  let src = "a 'b' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( false )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b'", "c", "d" ] );

  let src = "a 'b ' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( false )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b '", "c", "d" ] );

  let src = "a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( false )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b '", "c", "d" ] );

  let src = "'a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .preserving_delimeters( false )
  .preserving_empty( true )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "'a '", "b", "'c d" ] );

  /* */

  let src = "a 'b' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .preserving_delimeters( true )
  .preserving_empty( false )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b'", "c", "d" ] );

  let src = "a 'b ' c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .preserving_delimeters( true )
  .preserving_empty( false )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b '", "c", "d" ] );

  let src = "a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .preserving_delimeters( true )
  .preserving_empty( false )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "'b '", "c", "d" ] );

  let src = "'a 'b 'c d";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .preserving_delimeters( true )
  .preserving_empty( false )
  .quoting( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "'a '", "b", "'c d" ] );
}

//

fn _basic_split_with_vector()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "abc", ] );

  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![ "a", "b", "" ] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );

  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( vec![ "b", "d" ] )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

test_suite!
{
  basic,
  basic_form_and_methods,
  split_with_option_preserving_empty,
  split_with_option_preserving_delimeters,
  split_with_option_stripping,
  split_with_option_quoting,
  basic_split_with_vector,
}
<|endoftext|>


<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]
#![deny(trivial_casts)]

use std::cmp;
use std::env;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::Path;

mod draw;
use draw::draw;
use draw::get_absolute_draw_indices;
mod find;
use find::FindOptSubset;

extern crate ncurses;
use ncurses::*;

extern crate getopts;
use getopts::Options;

extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;

#[derive(Parser)]
#[grammar = "cmd.pest"]
struct IdentParser;

extern crate memmem;
use memmem::{Searcher, TwoWaySearcher};

#[derive(PartialEq, Copy, Clone)]
pub enum CursorSelects {
    LeftNibble,
    RightNibble,
    AsciiChar,
}

#[derive(Copy, Clone)]
pub struct CursorState {
    pos: usize,
    sel: CursorSelects,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursor = CursorState {
        pos: 0,
        sel: CursorSelects::LeftNibble,
    };
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const COLS: usize = 16;
    let mut command = String::new();
    let mut lastcommand = String::new();
    let mut autoparse = String::new();
    let mut infoline = String::new();

    // start ncursesw
    initscr();
    let screenheight = getmaxy(stdscr()) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    use_default_colors();
    init_pair(1, COLOR_GREEN, -1);

    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print the version");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            endwin();
            println!("{}", f.to_string());
            println!("Usage: {} FILE [options]", program);
            return;
        }
    };
    if matches.opt_present("v") {
        endwin();
        println!("Version: {}", VERSION);
        return;
    }
    if matches.opt_present("h") {
        endwin();
        println!("Usage: {} FILE [options]", program);
        return;
    }

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    let patharg = match matches.free.is_empty() {
        true => String::new(),
        false => matches.free[0].clone(),
    };
    let path = Path::new(&patharg);

    if patharg.is_empty() {
        endwin();
        println!("Patharg is empty!\n");
        return;
    }

    let mut file = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&path)
    {
        Err(why) => {
            endwin();
            println!("Could not open {}: {}", path.display(), why.to_string());
            return;
        }
        Ok(file) => file,
    };
    file.read_to_end(&mut buf)
        .ok()
        .expect("File could not be read.");

    let draw_range = get_absolute_draw_indices(buf.len(), COLS, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        COLS,
        &command,
        &mut infoline,
        cursor,
        screenoffset,
    );

    let mut quitnow = false;
    while quitnow == false {
        if autoparse.is_empty() {
            let key = std::char::from_u32(getch() as u32).unwrap();
            command.push_str(&key.clone().to_string());
        } else {
            command.push(autoparse.chars().nth(0).unwrap());
            autoparse.remove(0);
        }

        let parsethisstring = command.clone();
        let commands = IdentParser::parse(Rule::cmd_list, &parsethisstring)
            .unwrap_or_else(|e| panic!("{}", e));

        let mut clear = true;
        let mut save = false;
        for cmd in commands {
            match cmd.as_rule() {
                Rule::down => {
                    if cursor.pos + COLS < buf.len() {
                        // not at end
                        cursor.pos += COLS;
                    } else {
                        // when at end
                        cursor.pos = buf.len().saturating_sub(1);
                    }
                }
                Rule::up => {
                    if cursor.pos >= COLS {
                        cursor.pos -= COLS;
                    }
                }
                Rule::left => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos > 0 {
                            cursor.pos -= 1;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        if cursor.pos > 0 {
                            // not at start
                            cursor.sel = CursorSelects::RightNibble;
                            cursor.pos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    } else if cursor.sel == CursorSelects::RightNibble {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursor.pos - (cursor.pos % COLS) + (COLS - 1) < buf.len() {
                        // jump to end of line
                        cursor.pos = cursor.pos - (cursor.pos % COLS) + (COLS - 1);
                    } else {
                        // jump to end of line
                        cursor.pos = buf.len().saturating_sub(1);
                    }
                    if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    }
                }
                Rule::bottom => {
                    cursor.pos = buf.len().saturating_sub(1);
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                }
                Rule::replace => {
                    clear = false;
                }
                Rule::replacement => {
                    let key = command.chars().last().unwrap_or('x');
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        buf[cursor.pos] = key as u8;
                    } else {
                        let mask = if cursor.sel == CursorSelects::LeftNibble {
                            0x0F
                        } else {
                            0xF0
                        };
                        let shift = if cursor.sel == CursorSelects::LeftNibble {
                            4
                        } else {
                            0
                        };
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        // Change the selected nibble
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & mask | (c as u8) << shift;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::replaceend => {
                    clear = true;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        // remove the current char
                        buf.remove(cursor.pos);
                    }
                    // Move left if cursor is currently out of data
                    if cursor.pos >= buf.len() {
                        cursor.pos = cursor.pos.saturating_sub(1);
                    }
                    lastcommand = command.clone();
                }
                Rule::dd => {
                    let amount: usize = cmd.as_str().parse().unwrap_or(1);
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        let startofline = cursor.pos - cursor.pos % COLS;
                        let mut endofline = cursor.pos - (cursor.pos % COLS) + (COLS * amount);
                        endofline = cmp::min(endofline, buf.len());
                        buf.drain(startofline..endofline);
                        if cursor.pos >= buf.len() {
                            cursor.pos = buf.len().saturating_sub(1);
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::insert => {
                    // The next chars will be inserted
                    clear = false;
                }
                Rule::insertstuff => {
                    let key = command.chars().last().unwrap_or('x');

                    if cursor.sel == CursorSelects::LeftNibble {
                        // Left nibble
                        if let Some(c) = key.to_digit(16) {
                            buf.insert(cursor.pos, (c as u8) << 4);
                            cursor.sel = CursorSelects::RightNibble;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        // Right nibble
                        if cursor.pos == buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & 0xF0 | c as u8;
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::AsciiChar {
                        buf.insert(cursor.pos, key as u8);
                        cursor.pos += 1;
                    }

                    clear = false;
                }
                Rule::insertend => {
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::jumpascii => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else {
                        cursor.sel = CursorSelects::AsciiChar;
                    }
                }
                Rule::helpfile => {
                    command.pop();
                    command.push_str("No helpfile yet");
                    clear = false;
                }
                Rule::repeat => {
                    autoparse = lastcommand.clone();
                }
                Rule::gg => {
                    let linenr: usize = cmd.as_str().parse().unwrap_or(0);
                    cursor.pos = linenr * COLS; // jump to the line
                    if cursor.pos > buf.len() {
                        // detect file end
                        cursor.pos = buf.len();
                    }
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    clear = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,
                Rule::escape => (),
                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursor.pos = foundpos.search_in(&buf).unwrap_or(cursor.pos);
                    }
                    Rule::searchbytes => {
                        let search = inner_cmd.as_str().as_bytes();
                        let mut needle = vec![];
                        for i in 0..search.len() {
                            let nibble = match search[i] as u8 {
                                c @ 48..=57 => c - 48, // Numbers from 0 to 9
                                b'x' => 0x10,          // x is the wildcard
                                b'X' => 0x10,          // X is the wildcard
                                c @ b'a'..=b'f' => c - 87,
                                c @ b'A'..=b'F' => c - 55,
                                _ => panic!("Should not get to this position!"),
                            };
                            needle.push(nibble);
                        }
                        cursor.pos = buf.find_subset(&needle).unwrap_or(cursor.pos);
                        // infoline.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    Rule::gatherone => clear = false,
                    _ => {
                        command.push_str(&format!("no rule for {:?} ", inner_cmd.as_rule()));
                        clear = false;
                    }
                };
            }
            if save {
                if path.exists() {
                    let mut file = match OpenOptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(&path)
                    {
                        Err(why) => {
                            panic!("Could not open {}: {}", path.display(), why.to_string())
                        }
                        Ok(file) => file,
                    };
                    file.seek(SeekFrom::Start(0))
                        .ok()
                        .expect("Filepointer could not be set to 0");
                    file.write_all(&mut buf)
                        .ok()
                        .expect("File could not be written.");
                    file.set_len(buf.len() as u64)
                        .ok()
                        .expect("File could not be set to correct lenght.");
                    command.push_str("File saved!");
                } else {
                    command.push_str("Careful, file could not be saved!");
                }
                // TODO: define filename during runtime
                save = false;
            }
            if clear {
                command.clear();
            }

            // Always move screen when cursor leaves screen
            if cursor.pos > (screenheight + screenoffset - 1) * COLS - 1 {
                screenoffset = 2 + cursor.pos / COLS - screenheight;
            }
            if cursor.pos < screenoffset * COLS {
                screenoffset = cursor.pos / COLS;
            }
        }

        let draw_range = get_absolute_draw_indices(buf.len(), COLS, screenoffset);
        draw(
            &buf[draw_range.0..draw_range.1],
            COLS,
            &command,
            &mut infoline,
            cursor,
            screenoffset,
        );
    }

    refresh();
    endwin();
}
<commit_msg>Simplify condition in 'dd'<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]
#![deny(trivial_casts)]

use std::cmp;
use std::env;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::Path;

mod draw;
use draw::draw;
use draw::get_absolute_draw_indices;
mod find;
use find::FindOptSubset;

extern crate ncurses;
use ncurses::*;

extern crate getopts;
use getopts::Options;

extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;

#[derive(Parser)]
#[grammar = "cmd.pest"]
struct IdentParser;

extern crate memmem;
use memmem::{Searcher, TwoWaySearcher};

#[derive(PartialEq, Copy, Clone)]
pub enum CursorSelects {
    LeftNibble,
    RightNibble,
    AsciiChar,
}

#[derive(Copy, Clone)]
pub struct CursorState {
    pos: usize,
    sel: CursorSelects,
}

fn main() {
    const VERSION: &str = env!("CARGO_PKG_VERSION");
    let mut buf = vec![];
    let mut cursor = CursorState {
        pos: 0,
        sel: CursorSelects::LeftNibble,
    };
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const COLS: usize = 16;
    let mut command = String::new();
    let mut lastcommand = String::new();
    let mut autoparse = String::new();
    let mut infoline = String::new();

    // start ncursesw
    initscr();
    let screenheight = getmaxy(stdscr()) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    use_default_colors();
    init_pair(1, COLOR_GREEN, -1);

    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optflag("v", "version", "print the version");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            endwin();
            println!("{}", f.to_string());
            println!("Usage: {} FILE [options]", program);
            return;
        }
    };
    if matches.opt_present("v") {
        endwin();
        println!("Version: {}", VERSION);
        return;
    }
    if matches.opt_present("h") {
        endwin();
        println!("Usage: {} FILE [options]", program);
        return;
    }

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    let patharg = match matches.free.is_empty() {
        true => String::new(),
        false => matches.free[0].clone(),
    };
    let path = Path::new(&patharg);

    if patharg.is_empty() {
        endwin();
        println!("Patharg is empty!\n");
        return;
    }

    let mut file = match OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(&path)
    {
        Err(why) => {
            endwin();
            println!("Could not open {}: {}", path.display(), why.to_string());
            return;
        }
        Ok(file) => file,
    };
    file.read_to_end(&mut buf)
        .ok()
        .expect("File could not be read.");

    let draw_range = get_absolute_draw_indices(buf.len(), COLS, screenoffset);
    draw(
        &buf[draw_range.0..draw_range.1],
        COLS,
        &command,
        &mut infoline,
        cursor,
        screenoffset,
    );

    let mut quitnow = false;
    while quitnow == false {
        if autoparse.is_empty() {
            let key = std::char::from_u32(getch() as u32).unwrap();
            command.push_str(&key.clone().to_string());
        } else {
            command.push(autoparse.chars().nth(0).unwrap());
            autoparse.remove(0);
        }

        let parsethisstring = command.clone();
        let commands = IdentParser::parse(Rule::cmd_list, &parsethisstring)
            .unwrap_or_else(|e| panic!("{}", e));

        let mut clear = true;
        let mut save = false;
        for cmd in commands {
            match cmd.as_rule() {
                Rule::down => {
                    if cursor.pos + COLS < buf.len() {
                        // not at end
                        cursor.pos += COLS;
                    } else {
                        // when at end
                        cursor.pos = buf.len().saturating_sub(1);
                    }
                }
                Rule::up => {
                    if cursor.pos >= COLS {
                        cursor.pos -= COLS;
                    }
                }
                Rule::left => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos > 0 {
                            cursor.pos -= 1;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        if cursor.pos > 0 {
                            // not at start
                            cursor.sel = CursorSelects::RightNibble;
                            cursor.pos -= 1;
                        }
                    }
                }
                Rule::right => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    } else if cursor.sel == CursorSelects::RightNibble {
                        if cursor.pos + 1 < buf.len() {
                            // not at end
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    }
                }
                Rule::start => {
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    if cursor.sel == CursorSelects::RightNibble {
                        cursor.sel = CursorSelects::LeftNibble;
                    }
                }
                Rule::end => {
                    // check if no overflow
                    if cursor.pos - (cursor.pos % COLS) + (COLS - 1) < buf.len() {
                        // jump to end of line
                        cursor.pos = cursor.pos - (cursor.pos % COLS) + (COLS - 1);
                    } else {
                        // jump to end of line
                        cursor.pos = buf.len().saturating_sub(1);
                    }
                    if cursor.sel == CursorSelects::LeftNibble {
                        cursor.sel = CursorSelects::RightNibble;
                    }
                }
                Rule::bottom => {
                    cursor.pos = buf.len().saturating_sub(1);
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                }
                Rule::replace => {
                    clear = false;
                }
                Rule::replacement => {
                    let key = command.chars().last().unwrap_or('x');
                    if cursor.sel == CursorSelects::AsciiChar {
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        buf[cursor.pos] = key as u8;
                    } else {
                        let mask = if cursor.sel == CursorSelects::LeftNibble {
                            0x0F
                        } else {
                            0xF0
                        };
                        let shift = if cursor.sel == CursorSelects::LeftNibble {
                            4
                        } else {
                            0
                        };
                        if cursor.pos >= buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        // Change the selected nibble
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & mask | (c as u8) << shift;
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::replaceend => {
                    clear = true;
                }
                Rule::remove => {
                    // check if in valid range
                    if buf.len() > 0 && cursor.pos < buf.len() {
                        // remove the current char
                        buf.remove(cursor.pos);
                    }
                    // Move left if cursor is currently out of data
                    if cursor.pos >= buf.len() {
                        cursor.pos = cursor.pos.saturating_sub(1);
                    }
                    lastcommand = command.clone();
                }
                Rule::dd => {
                    let amount: usize = cmd.as_str().parse().unwrap_or(1);
                    if cursor.pos < buf.len() {
                        let startofline = cursor.pos - cursor.pos % COLS;
                        let mut endofline = cursor.pos - (cursor.pos % COLS) + (COLS * amount);
                        endofline = cmp::min(endofline, buf.len());
                        buf.drain(startofline..endofline);
                        if cursor.pos >= buf.len() {
                            cursor.pos = buf.len().saturating_sub(1);
                        }
                    }
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::insert => {
                    // The next chars will be inserted
                    clear = false;
                }
                Rule::insertstuff => {
                    let key = command.chars().last().unwrap_or('x');

                    if cursor.sel == CursorSelects::LeftNibble {
                        // Left nibble
                        if let Some(c) = key.to_digit(16) {
                            buf.insert(cursor.pos, (c as u8) << 4);
                            cursor.sel = CursorSelects::RightNibble;
                        }
                    } else if cursor.sel == CursorSelects::RightNibble {
                        // Right nibble
                        if cursor.pos == buf.len() {
                            buf.insert(cursor.pos, 0);
                        }
                        if let Some(c) = key.to_digit(16) {
                            buf[cursor.pos] = buf[cursor.pos] & 0xF0 | c as u8;
                            cursor.sel = CursorSelects::LeftNibble;
                            cursor.pos += 1;
                        }
                    } else if cursor.sel == CursorSelects::AsciiChar {
                        buf.insert(cursor.pos, key as u8);
                        cursor.pos += 1;
                    }

                    clear = false;
                }
                Rule::insertend => {
                    lastcommand = command.clone();
                    clear = true;
                }
                Rule::jumpascii => {
                    if cursor.sel == CursorSelects::AsciiChar {
                        cursor.sel = CursorSelects::LeftNibble;
                    } else {
                        cursor.sel = CursorSelects::AsciiChar;
                    }
                }
                Rule::helpfile => {
                    command.pop();
                    command.push_str("No helpfile yet");
                    clear = false;
                }
                Rule::repeat => {
                    autoparse = lastcommand.clone();
                }
                Rule::gg => {
                    let linenr: usize = cmd.as_str().parse().unwrap_or(0);
                    cursor.pos = linenr * COLS; // jump to the line
                    if cursor.pos > buf.len() {
                        // detect file end
                        cursor.pos = buf.len();
                    }
                    cursor.pos -= cursor.pos % COLS; // jump to start of line
                    clear = true;
                }
                Rule::backspace => {
                    command.pop();
                    command.pop();
                    clear = false;
                }
                Rule::saveandexit => {
                    save = true;
                    quitnow = true;
                }
                Rule::exit => quitnow = true,
                Rule::save => save = true,
                Rule::escape => (),
                _ => (),
            }

            for inner_cmd in cmd.into_inner() {
                match inner_cmd.as_rule() {
                    Rule::searchstr => {
                        let search = inner_cmd.as_str().as_bytes();
                        let foundpos = TwoWaySearcher::new(&search);
                        cursor.pos = foundpos.search_in(&buf).unwrap_or(cursor.pos);
                    }
                    Rule::searchbytes => {
                        let search = inner_cmd.as_str().as_bytes();
                        let mut needle = vec![];
                        for i in 0..search.len() {
                            let nibble = match search[i] as u8 {
                                c @ 48..=57 => c - 48, // Numbers from 0 to 9
                                b'x' => 0x10,          // x is the wildcard
                                b'X' => 0x10,          // X is the wildcard
                                c @ b'a'..=b'f' => c - 87,
                                c @ b'A'..=b'F' => c - 55,
                                _ => panic!("Should not get to this position!"),
                            };
                            needle.push(nibble);
                        }
                        cursor.pos = buf.find_subset(&needle).unwrap_or(cursor.pos);
                        // infoline.push_str(&format!("Searching for: {:?}", needle ));
                    }
                    Rule::gatherone => clear = false,
                    _ => {
                        command.push_str(&format!("no rule for {:?} ", inner_cmd.as_rule()));
                        clear = false;
                    }
                };
            }
            if save {
                if path.exists() {
                    let mut file = match OpenOptions::new()
                        .read(true)
                        .write(true)
                        .create(true)
                        .open(&path)
                    {
                        Err(why) => {
                            panic!("Could not open {}: {}", path.display(), why.to_string())
                        }
                        Ok(file) => file,
                    };
                    file.seek(SeekFrom::Start(0))
                        .ok()
                        .expect("Filepointer could not be set to 0");
                    file.write_all(&mut buf)
                        .ok()
                        .expect("File could not be written.");
                    file.set_len(buf.len() as u64)
                        .ok()
                        .expect("File could not be set to correct lenght.");
                    command.push_str("File saved!");
                } else {
                    command.push_str("Careful, file could not be saved!");
                }
                // TODO: define filename during runtime
                save = false;
            }
            if clear {
                command.clear();
            }

            // Always move screen when cursor leaves screen
            if cursor.pos > (screenheight + screenoffset - 1) * COLS - 1 {
                screenoffset = 2 + cursor.pos / COLS - screenheight;
            }
            if cursor.pos < screenoffset * COLS {
                screenoffset = cursor.pos / COLS;
            }
        }

        let draw_range = get_absolute_draw_indices(buf.len(), COLS, screenoffset);
        draw(
            &buf[draw_range.0..draw_range.1],
            COLS,
            &command,
            &mut infoline,
            cursor,
            screenoffset,
        );
    }

    refresh();
    endwin();
}
<|endoftext|>