<commit_before>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx0 = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx0.is_null());
        allow_syscall(ctx0, __NR_execve);
        allow_syscall(ctx0, __NR_brk);
        allow_syscall(ctx0, __NR_access);
        allow_syscall(ctx0, __NR_mmap);
        allow_syscall(ctx0, __NR_open);
        allow_syscall(ctx0, __NR_read);
        allow_syscall(ctx0, __NR_write);
        allow_syscall(ctx0, __NR_stat);
        allow_syscall(ctx0, __NR_fstat);
        allow_syscall(ctx0, __NR_close);
        allow_syscall(ctx0, __NR_mprotect);
        allow_syscall(ctx0, __NR_arch_prctl);
        allow_syscall(ctx0, __NR_munmap);
        allow_syscall(ctx0, __NR_set_tid_address);
        allow_syscall(ctx0, __NR_set_robust_list);
        allow_syscall(ctx0, __NR_rt_sigaction);
        allow_syscall(ctx0, __NR_rt_sigprocmask);
        allow_syscall(ctx0, __NR_getrlimit);
        allow_syscall(ctx0, __NR_statfs);
        allow_syscall(ctx0, __NR_fcntl);
        allow_syscall(ctx0, __NR_geteuid);
        allow_syscall(ctx0, __NR_umask);
        allow_syscall(ctx0, __NR_pipe);
        allow_syscall(ctx0, __NR_clone);
        allow_syscall(ctx0, __NR_wait4);
        allow_syscall(ctx0, __NR_exit_group);
        allow_syscall(ctx0, __NR_dup);
        allow_syscall(ctx0, __NR_ioctl);
        allow_syscall(ctx0, __NR_getpid);
        allow_syscall(ctx0, __NR_gettid);
        allow_syscall(ctx0, __NR_tgkill);
        allow_syscall(ctx0, __NR_rt_sigreturn);
        allow_syscall(ctx0, __NR_socket);
        allow_syscall(ctx0, __NR_connect);
        allow_syscall(ctx0, __NR_lseek);
        allow_syscall(ctx0, __NR_openat);
        allow_syscall(ctx0, __NR_unlinkat);
        allow_syscall(ctx0, __NR_utimensat);
        allow_syscall(ctx0, __NR_lstat);
        allow_syscall(ctx0, __NR_fchmod);
        allow_syscall(ctx0, __NR_unlink);
        allow_syscall(ctx0, __NR_utime);
        allow_syscall(ctx0, __NR_futex);
        allow_syscall(ctx0, __NR_chmod);
        allow_syscall(ctx0, __NR_getuid);
        allow_syscall(ctx0, __NR_getgid);
        allow_syscall(ctx0, __NR_getppid);
        allow_syscall(ctx0, __NR_getcwd);
        allow_syscall(ctx0, __NR_getegid);
        allow_syscall(ctx0, __NR_getdents);
        allow_syscall(ctx0, __NR_readlink);
        assert!(seccomp_load(ctx0) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn path_to_cstring(path : &Path) -> CString {
    return CString::new(path.as_os_str().as_bytes()).unwrap();
}

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, path_to_cstring(path0).as_ptr(), libc::AT_FDCWD, path_to_cstring(path1).as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".7z",
            ],
            extract_cmd: vec!["7z", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                if !arch.as_os_str().as_bytes().ends_with(ext.as_bytes()) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch.as_os_str().as_bytes()[0..arch.as_os_str().as_bytes().len()-ext.as_bytes().len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(&arch)
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<commit_msg>Strip .part1.rar from multipart rar archives<commit_after>#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;

extern crate libc;
extern crate scmp;
extern crate tempdir;

use std::fs;
use std::env;
use std::ffi::OsStr;
use std::ffi::CString;
use std::io::Error;
use std::os::raw::c_void;
use std::os::unix::process::CommandExt;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use scmp::*;

unsafe fn allow_syscall(ctx : *mut c_void, nr : u32) {
    assert!(seccomp_rule_add_array(ctx, SCMP_ACT_ALLOW, nr as i32, 0, std::ptr::null()) == 0);
}

fn setup_seccomp() -> Result<(), Error> {
    unsafe {
        let ctx0 = seccomp_init(SCMP_ACT_TRAP);
        assert!(!ctx0.is_null());
        allow_syscall(ctx0, __NR_execve);
        allow_syscall(ctx0, __NR_brk);
        allow_syscall(ctx0, __NR_access);
        allow_syscall(ctx0, __NR_mmap);
        allow_syscall(ctx0, __NR_open);
        allow_syscall(ctx0, __NR_read);
        allow_syscall(ctx0, __NR_write);
        allow_syscall(ctx0, __NR_stat);
        allow_syscall(ctx0, __NR_fstat);
        allow_syscall(ctx0, __NR_close);
        allow_syscall(ctx0, __NR_mprotect);
        allow_syscall(ctx0, __NR_arch_prctl);
        allow_syscall(ctx0, __NR_munmap);
        allow_syscall(ctx0, __NR_set_tid_address);
        allow_syscall(ctx0, __NR_set_robust_list);
        allow_syscall(ctx0, __NR_rt_sigaction);
        allow_syscall(ctx0, __NR_rt_sigprocmask);
        allow_syscall(ctx0, __NR_getrlimit);
        allow_syscall(ctx0, __NR_statfs);
        allow_syscall(ctx0, __NR_fcntl);
        allow_syscall(ctx0, __NR_geteuid);
        allow_syscall(ctx0, __NR_umask);
        allow_syscall(ctx0, __NR_pipe);
        allow_syscall(ctx0, __NR_clone);
        allow_syscall(ctx0, __NR_wait4);
        allow_syscall(ctx0, __NR_exit_group);
        allow_syscall(ctx0, __NR_dup);
        allow_syscall(ctx0, __NR_ioctl);
        allow_syscall(ctx0, __NR_getpid);
        allow_syscall(ctx0, __NR_gettid);
        allow_syscall(ctx0, __NR_tgkill);
        allow_syscall(ctx0, __NR_rt_sigreturn);
        allow_syscall(ctx0, __NR_socket);
        allow_syscall(ctx0, __NR_connect);
        allow_syscall(ctx0, __NR_lseek);
        allow_syscall(ctx0, __NR_openat);
        allow_syscall(ctx0, __NR_unlinkat);
        allow_syscall(ctx0, __NR_utimensat);
        allow_syscall(ctx0, __NR_lstat);
        allow_syscall(ctx0, __NR_fchmod);
        allow_syscall(ctx0, __NR_unlink);
        allow_syscall(ctx0, __NR_utime);
        allow_syscall(ctx0, __NR_futex);
        allow_syscall(ctx0, __NR_chmod);
        allow_syscall(ctx0, __NR_getuid);
        allow_syscall(ctx0, __NR_getgid);
        allow_syscall(ctx0, __NR_getppid);
        allow_syscall(ctx0, __NR_getcwd);
        allow_syscall(ctx0, __NR_getegid);
        allow_syscall(ctx0, __NR_getdents);
        allow_syscall(ctx0, __NR_readlink);
        assert!(seccomp_load(ctx0) == 0);
    }
    return Ok(());
}

const RENAME_NOREPLACE : u64 = 1;

fn path_to_cstring(path : &Path) -> CString {
    return CString::new(path.as_os_str().as_bytes()).unwrap();
}

fn rename_noreplace(path0 : &Path, path1 : &Path) -> Result<(), Error> {
    unsafe {
        if libc::syscall(__NR_renameat2 as i64, libc::AT_FDCWD, path_to_cstring(path0).as_ptr(), libc::AT_FDCWD, path_to_cstring(path1).as_ptr(), RENAME_NOREPLACE, 0) == 0 {
            return Ok(());
        } else {
            return Err(Error::last_os_error());
        }
    }
}

fn rename_or_suffix(path0 : &Path, path1 : &Path) -> Result<PathBuf, Error> {
    let rv = rename_noreplace(path0, path1);
    if let Err(err) = rv {
        if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
            return Err(err);
        }
        for suffix in 1.. {
            let mut builder = path1.as_os_str().to_os_string();
            builder.push(format!(".{}", suffix));
            let rv = rename_noreplace(path0, Path::new(&builder));
            if let Err(err) = rv {
                if err.raw_os_error().expect("Error fails to wrap errno") != libc::EEXIST {
                    return Err(err);
                }
                continue;
            }
            return Ok(Path::new(&builder).to_path_buf());
        }
        return Ok(PathBuf::new()); // XXX Unreachable
    } else {
        return Ok(path1.to_path_buf());
    }
}

struct ArchiveType {
    extensions: Vec<&'static str>,
    extract_cmd: Vec<&'static str>,
}

lazy_static! {
    static ref ARCHIVE_TYPES : Vec<ArchiveType> = vec![
        ArchiveType {
            extensions: vec![
                ".tgz",
                ".tar.gz",
                ".tar.Z",
                ".tbz2",
                ".tar.bz2",
                ".tlz",
                ".tar.lz",
                ".tar.lzma",
                ".tar.lzo",
                ".tar.xz",
            ],
            extract_cmd: vec!["tar", "-xf"],
        },
        ArchiveType {
            extensions: vec![
                ".zip",
            ],
            extract_cmd: vec!["unzip", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".part1.rar",
                ".rar",
            ],
            extract_cmd: vec!["unrar", "x", "--"],
        },
        ArchiveType {
            extensions: vec![
                ".7z",
            ],
            extract_cmd: vec!["7z", "x", "--"],
        },
    ];
}

fn main() {
    let matches = clap_app!(myapp =>
        (@arg ARCHIVE: +required ... "Archives to extract")
    ).get_matches();

    let mut found = false;

    for arch in matches.values_of_os("ARCHIVE").unwrap() {
        let arch = Path::new(arch);
        println!("Extract {:?}", arch);
        for at in ARCHIVE_TYPES.iter() {
            for &ext in &at.extensions {
                if !arch.as_os_str().as_bytes().ends_with(ext.as_bytes()) {
                    continue;
                }
                found = true;

                let noext : &OsStr = OsStrExt::from_bytes(&arch.as_os_str().as_bytes()[0..arch.as_os_str().as_bytes().len()-ext.as_bytes().len()]);
                let stripped =
                    if let Some(noext) = Path::new(noext).file_name() {
                        noext
                    } else {
                        OsStr::new("extracted")
                    };
                let cmd = at.extract_cmd[0];
                let tmpdir = tempdir::TempDir::new_in(
                    env::current_dir().expect("Unable to get the current directory"),
                    stripped.to_string_lossy().to_mut().as_str()).expect("Unable to create temporary directory");
                let status = Command::new(cmd)
                    .args(&at.extract_cmd[1..])
                    .arg(&arch)
                    .current_dir(&tmpdir)
                    .before_exec(setup_seccomp)
                    .status()
                    .expect(format!("Failed to launch {}", cmd).as_str());
                assert!(status.success(), format!("{} wasn't successful", cmd));
                let extracted_path = tmpdir.into_path();
                let mut dir_list = fs::read_dir(&extracted_path).expect("Unable to read the extraction directory");
                if let Some(path) = dir_list.next() {
                    let path = path.expect("Unable to iterate the extraction directory");
                    if let Some(path1) = dir_list.next() {
                        path1.expect("Unable to iterate the extraction directory");
                        let newname = rename_or_suffix(&extracted_path, Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                    } else {
                        let newname = rename_or_suffix(&path.path(), Path::new(stripped)).expect("Failed to rename");
                        println!("Extracted to {:?}", newname);
                        fs::remove_dir(&extracted_path).expect("Failed to remove empty extraction directory");
                    }
                } else {
                    println!("No files were extracted");
                }
                break;
            }
        }
        if !found {
            println!("Can't handle {:?}", arch);
        }
    }
}

<|endoftext|>


<commit_before>use instr::Instr;
use cpu::Cpu;
use cpu::CpuInterrupt;
use time::precise_time_ns;
use std::fs::File;
use std::io::Read;
use std::ops::Index;
use std::cmp::Ordering;
use std::collections::BinaryHeap;

use glium::DisplayBuild;
use glium::Surface;
use glium::SwapBuffersError;
use glium::glutin::Api;
use glium::glutin::GlRequest;
use glium::glutin::Event;

extern crate time;
extern crate getopts;
#[macro_use]
extern crate glium;
extern crate nalgebra;

mod instr;
mod cpu;
mod render;

#[derive(Copy, Clone)]
pub enum MemSection {
    Vram,
    RomBank0,
}

pub struct AddressSpace {
    data: [u8; 0x10000],
}

impl AddressSpace {

    pub fn new() -> AddressSpace {
        AddressSpace { data: [0; 0x10000] }
    }

    pub fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    pub fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        &self.data[addr as usize .. (addr + bytes) as usize]
    }

    pub fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    pub fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

    pub fn load_rom(&mut self, rom: &mut File) -> std::io::Result<()> {
        // Read in header first
        try!(rom.read(&mut self.data[0x000..0x150]));
        // Then read in remaining cart data
        try!(rom.read(&mut self.data[0x0150..0x8000]));
        Ok(())
    }

}

impl Index<u16> for AddressSpace {
    type Output = u8;

    fn index(&self, idx: u16) -> &u8 {
        &self.data[idx as usize]
    }
}

#[derive(Copy, Clone)]
pub enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy, Clone)]
pub enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

pub struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    pub fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    pub fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    pub fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    pub fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    pub fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    pub fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    pub fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    pub fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    pub fn get_flag(&self, flag: RegFlag) -> bool {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        self.flag & bit != 0
    }

    pub fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

    pub fn set_pc(&mut self, addr: u16) -> u16 {
        let pc = self.pc;
        self.pc = addr;
        pc
    }

    pub fn add_pc(&mut self, n: i8) -> u16 {
        let pc = self.pc;
        self.pc = ((self.pc as i32) + (n as i32)) as u16;
        pc
    }

}

#[derive(Copy, Clone)]
pub enum IntType {
    VblankStart,
    VblankEnd,
}

struct ClockInt {
    pub int_target: u64,
    pub int_type: IntType,
}

impl PartialEq for ClockInt {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.int_target == other.int_target
    }
}

impl Eq for ClockInt {}

impl PartialOrd for ClockInt {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.int_target.partial_cmp(&other.int_target) {
            Some(Ordering::Less) => Some(Ordering::Greater),
            Some(Ordering::Greater) => Some(Ordering::Less),
            ord => ord,
        }
    }
}

impl Ord for ClockInt {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        match self.int_target.cmp(&other.int_target) {
            Ordering::Less => Ordering::Greater,
            Ordering::Greater => Ordering::Less,
            ord => ord,
        }
    }
}

const NS_PER_S: u64 = 1_000_000_000;
const MS_PER_NS: u64 = 1_000_000;

// 10ms
const BUSY_WAIT_THRESHOLD: u64 = 10_000_000;

pub struct Clock {
    freq:       u32,
    period:     u64,
    int_heap:   BinaryHeap<ClockInt>,
}

impl Clock {

    pub fn new(freq: u32) -> Clock {
        Clock {
            freq: freq,
            period: NS_PER_S / (freq as u64),
            int_heap: BinaryHeap::new(),
        }
    }

    pub fn set_interrupt(&mut self, itype: IntType, period: u64) {
        let start = precise_time_ns();
        let int = ClockInt {
            int_type: itype,
            int_target: start + period,
        };
        self.int_heap.push(int);
    }

    pub fn wait_cycles(&mut self, n: u32) -> Option<IntType> {
        let start = precise_time_ns();
        let real_wait = self.period * (n as u64);
        let real_target = real_wait + start;
        let (target, result) = if let Some(interrupt) = self.int_heap.pop() {
            if real_target > interrupt.int_target {
                (interrupt.int_target, Some(interrupt.int_type))
            } else {
                self.int_heap.push(interrupt);
                (real_target, None)
            }
        } else {
            (real_target, None)
        };
        let mut curtime = start;
        if target > start && target - start > BUSY_WAIT_THRESHOLD {
            std::thread::sleep_ms(((target - start) * MS_PER_NS) as u32);
            return result;
        } else {
            loop {
                if curtime >= target {
                    return result;
                }
                curtime = precise_time_ns();
            }
        }
    }

}

fn main() {
    //  Gather command line args
    let args: Vec<String> = std::env::args().collect();
    let mut opts = getopts::Options::new();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(e) => panic!("Error: {}", e),
    };
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("No input ROM");
        return;
    };

    // Build graphics context and window
    let display = glium::glutin::WindowBuilder::new()
        .with_title("Gameboy Rust".to_string())
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
        .build_glium()
        .unwrap();

    // Do machine initialization
    let mut cpu = Cpu::new();
    {
        let mut ram = cpu.get_ram();
        let mut romfile = match File::open(std::path::Path::new(&input)) {
            Ok(f) => { f },
            Err(e) => {
                println!("Error opening file: {}", e);
                return;
            }
        };
        if let Err(e) = ram.load_rom(&mut romfile) {
            println!("Error loading rom data: {}", e);
            return;
        }
    }

    // Initialize virtual LCD
    let mut lcd = render::GbDisplay::new(&display);

    let mut viewport = {
        let window = display.get_window();
        let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
        render::calculate_viewport(width, height)
    };

    // Initialize virtual hardware clocks
    let mut clock = Clock::new(cpu::GB_FREQUENCY);
    clock.set_interrupt(IntType::VblankStart, render::VBLANK_PERIOD);

    // Simulate CPU
    'main: loop {
        // Collect user input
        for ev in display.poll_events() {
            match ev {
                Event::Closed => {
                    break 'main;
                },
                Event::Resized(..) => {
                    let window = display.get_window();
                    let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
                    viewport = render::calculate_viewport(width, height);
                },
                _ => (),
            }
        }

        // Simulate CPU and hardware timers
        'sim: loop  {
            if let Some(int) = clock.wait_cycles(cpu.do_instr()) {
                // Handle timer interrupt
                match int {
                    // Interrupt at the start of the vblank period
                    IntType::VblankStart => {
                        clock.set_interrupt(IntType::VblankStart, render::VBLANK_PERIOD);
                        clock.set_interrupt(IntType::VblankEnd, render::VBLANK_DURATION);
                        cpu.interrupt(CpuInterrupt::Vblank);
                    }
                    // At the end, collect data from VRAM and render it
                    IntType::VblankEnd => {
                        break 'sim;
                    }
                }
            }
        }

        // Redraw screen
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        lcd.clear_viewport(&mut target, viewport, (1.0, 1.0, 1.0, 0.0));
        match target.finish().err() {
            Some(SwapBuffersError::ContextLost) => {
                panic!("OpenGL contetxt lost!");
            },
            Some(SwapBuffersError::AlreadySwapped) => {
                println!("Warning: OpenGL buffer already swapped");
            },
            None => (),
        }
    }
}
<commit_msg>Attempt rendering after vblank<commit_after>use instr::Instr;
use cpu::Cpu;
use cpu::CpuInterrupt;
use time::precise_time_ns;
use std::fs::File;
use std::io::Read;
use std::ops::Index;
use std::cmp::Ordering;
use std::collections::BinaryHeap;

use glium::DisplayBuild;
use glium::Surface;
use glium::SwapBuffersError;
use glium::glutin::Api;
use glium::glutin::GlRequest;
use glium::glutin::Event;

extern crate time;
extern crate getopts;
#[macro_use]
extern crate glium;
extern crate nalgebra;

mod instr;
mod cpu;
mod render;

#[derive(Copy, Clone)]
pub enum MemSection {
    Vram,
    RomBank0,
}

pub struct AddressSpace {
    data: [u8; 0x10000],
}

impl AddressSpace {

    pub fn new() -> AddressSpace {
        AddressSpace { data: [0; 0x10000] }
    }

    pub fn read(&self, addr: u16) -> u8 {
        self.data[addr as usize]
    }

    pub fn read_u16(&self, addr: u16) -> u16 {
        self.data[addr as usize] as u16 | ((self.data[addr as usize + 1] as u16) << 8)
    }

    pub fn read_slice(&self, addr: u16, bytes: u16) -> &[u8] {
        &self.data[addr as usize .. (addr + bytes) as usize]
    }

    pub fn write(&mut self, addr: u16, data: u8) {
        self.data[addr as usize] = data;
    }

    pub fn write_u16(&mut self, addr: u16, data: u16) {
        let lo = (data & 0xFF) as u8;
        let hi = ((data & 0xFF00) >> 8) as u8;
        self.data[addr as usize] = lo;
        self.data[addr as usize + 1] = hi;
    }

    pub fn load_rom(&mut self, rom: &mut File) -> std::io::Result<()> {
        // Read in header first
        try!(rom.read(&mut self.data[0x000..0x150]));
        // Then read in remaining cart data
        try!(rom.read(&mut self.data[0x0150..0x8000]));
        Ok(())
    }

}

impl Index<u16> for AddressSpace {
    type Output = u8;

    fn index(&self, idx: u16) -> &u8 {
        &self.data[idx as usize]
    }
}

#[derive(Copy, Clone)]
pub enum RegFlag {
    Zero,
    Subtract,
    HalfCarry,
    Carry,
}

#[derive(Copy, Clone)]
pub enum Register {
    A,
    B,
    C,
    D,
    E,
    F,
    H,
    L,
    AF,
    BC,
    DE,
    HL,
    SP,
    PC,
    Flag,
}

pub struct RegData {
    a: u8,
    b: u8,
    c: u8,
    d: u8,
    e: u8,
    f: u8,
    h: u8,
    l: u8,
    sp: u16,
    pc: u16,
    flag: u8,
}

impl RegData {

    pub fn new() -> RegData {
        RegData {
            a: 0,
            b: 0,
            c: 0,
            d: 0,
            e: 0,
            f: 0,
            h: 0,
            l: 0,
            sp: 0xFFFE,
            pc: 0x100,
            flag: 0,
        }
    }

    pub fn read(&self, reg: Register) -> u8 {
        match reg {
            Register::A => self.a,
            Register::B => self.b,
            Register::C => self.c,
            Register::D => self.d,
            Register::E => self.e,
            Register::F => self.f,
            Register::H => self.h,
            Register::L => self.l,
            Register::Flag => self.flag,
            _ => panic!("Register not available for 8-bit read"),
        }
    }

    pub fn read_u16(&self, reg: Register) -> u16 {
        match reg {
            Register::AF => (self.a as u16) << 8 | self.f as u16,
            Register::BC => (self.b as u16) << 8 | self.c as u16,
            Register::DE => (self.d as u16) << 8 | self.e as u16,
            Register::HL => (self.h as u16) << 8 | self.l as u16,
            Register::SP => self.sp,
            Register::PC => self.pc,
            _ => panic!("Register not available for 16-bit read"),
        }
    }

    pub fn write(&mut self, reg: Register, data: u8) {
        match reg {
            Register::A => self.a = data,
            Register::B => self.b = data,
            Register::C => self.c = data,
            Register::D => self.d = data,
            Register::E => self.e = data,
            Register::F => self.f = data,
            Register::H => self.h = data,
            Register::L => self.l = data,
            _ => panic!("Register not available for 8-bit write"),
        }
    }

    pub fn write_u16(&mut self, reg: Register, data: u16) {
        let (hi, lo) = ((data & 0xFF00 >> 8) as u8, (data & 0xFF) as u8);
        match reg {
            Register::AF => {
                self.a = hi;
                self.f = lo;
            },
            Register::BC => {
                self.b = hi;
                self.c = lo;
            },
            Register::DE => {
                self.d = hi;
                self.c = lo;
            },
            Register::HL => {
                self.h = hi;
                self.l = lo;
            },
            Register::SP => self.sp = data,
            Register::PC => self.pc = data,
            _ => panic!("Register not available for 16-bit write"),
        }
    }

    pub fn copy(&mut self, dst: Register, src: Register) {
        let data = self.read(src);
        self.write(dst, data);
    }

    pub fn copy_u16(&mut self, dst: Register, src: Register) {
        let data = self.read_u16(src);
        self.write_u16(dst, data);
    }

    pub fn set_flag(&mut self, flag: RegFlag, on: bool) {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        if (on) {
            self.flag |= bit;
        } else {
            self.flag &= bit ^ 0xFF;
        }
    }

    pub fn get_flag(&self, flag: RegFlag) -> bool {
        let bit = match flag {
            RegFlag::Zero => 0x80,
            RegFlag::Subtract => 0x40,
            RegFlag::HalfCarry => 0x20,
            RegFlag::Carry => 0x10,
        };
        self.flag & bit != 0
    }

    pub fn advance_pc(&mut self) -> u16 {
        let pc = self.pc;
        self.pc += 1;
        pc
    }

    pub fn set_pc(&mut self, addr: u16) -> u16 {
        let pc = self.pc;
        self.pc = addr;
        pc
    }

    pub fn add_pc(&mut self, n: i8) -> u16 {
        let pc = self.pc;
        self.pc = ((self.pc as i32) + (n as i32)) as u16;
        pc
    }

}

#[derive(Copy, Clone)]
pub enum IntType {
    VblankStart,
    VblankEnd,
}

struct ClockInt {
    pub int_target: u64,
    pub int_type: IntType,
}

impl PartialEq for ClockInt {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.int_target == other.int_target
    }
}

impl Eq for ClockInt {}

impl PartialOrd for ClockInt {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match self.int_target.partial_cmp(&other.int_target) {
            Some(Ordering::Less) => Some(Ordering::Greater),
            Some(Ordering::Greater) => Some(Ordering::Less),
            ord => ord,
        }
    }
}

impl Ord for ClockInt {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        match self.int_target.cmp(&other.int_target) {
            Ordering::Less => Ordering::Greater,
            Ordering::Greater => Ordering::Less,
            ord => ord,
        }
    }
}

const NS_PER_S: u64 = 1_000_000_000;
const MS_PER_NS: u64 = 1_000_000;

// 10ms
const BUSY_WAIT_THRESHOLD: u64 = 10_000_000;

pub struct Clock {
    freq:       u32,
    period:     u64,
    int_heap:   BinaryHeap<ClockInt>,
}

impl Clock {

    pub fn new(freq: u32) -> Clock {
        Clock {
            freq: freq,
            period: NS_PER_S / (freq as u64),
            int_heap: BinaryHeap::new(),
        }
    }

    pub fn set_interrupt(&mut self, itype: IntType, period: u64) {
        let start = precise_time_ns();
        let int = ClockInt {
            int_type: itype,
            int_target: start + period,
        };
        self.int_heap.push(int);
    }

    pub fn wait_cycles(&mut self, n: u32) -> Option<IntType> {
        let start = precise_time_ns();
        let real_wait = self.period * (n as u64);
        let real_target = real_wait + start;
        let (target, result) = if let Some(interrupt) = self.int_heap.pop() {
            if real_target > interrupt.int_target {
                (interrupt.int_target, Some(interrupt.int_type))
            } else {
                self.int_heap.push(interrupt);
                (real_target, None)
            }
        } else {
            (real_target, None)
        };
        let mut curtime = start;
        if target > start && target - start > BUSY_WAIT_THRESHOLD {
            std::thread::sleep_ms(((target - start) * MS_PER_NS) as u32);
            return result;
        } else {
            loop {
                if curtime >= target {
                    return result;
                }
                curtime = precise_time_ns();
            }
        }
    }

}

fn main() {
    //  Gather command line args
    let args: Vec<String> = std::env::args().collect();
    let mut opts = getopts::Options::new();
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m },
        Err(e) => panic!("Error: {}", e),
    };
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        println!("No input ROM");
        return;
    };

    // Build graphics context and window
    let display = glium::glutin::WindowBuilder::new()
        .with_title("Gameboy Rust".to_string())
        .with_gl(GlRequest::Specific(Api::OpenGl, (3, 2)))
        .build_glium()
        .unwrap();

    // Do machine initialization
    let mut cpu = Cpu::new();
    {
        let mut ram = cpu.get_ram();
        let mut romfile = match File::open(std::path::Path::new(&input)) {
            Ok(f) => { f },
            Err(e) => {
                println!("Error opening file: {}", e);
                return;
            }
        };
        if let Err(e) = ram.load_rom(&mut romfile) {
            println!("Error loading rom data: {}", e);
            return;
        }
    }

    // Initialize virtual LCD
    let mut lcd = render::GbDisplay::new(&display);

    let mut viewport = {
        let window = display.get_window();
        let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
        render::calculate_viewport(width, height)
    };

    // Initialize virtual hardware clocks
    let mut clock = Clock::new(cpu::GB_FREQUENCY);
    clock.set_interrupt(IntType::VblankStart, render::VBLANK_PERIOD);

    // Simulate CPU
    'main: loop {
        // Collect user input
        for ev in display.poll_events() {
            match ev {
                Event::Closed => {
                    break 'main;
                },
                Event::Resized(..) => {
                    let window = display.get_window();
                    let (width, height) = window.unwrap().get_inner_size_pixels().unwrap();
                    viewport = render::calculate_viewport(width, height);
                },
                _ => (),
            }
        }

        // Simulate CPU and hardware timers
        'sim: loop  {
            if let Some(int) = clock.wait_cycles(cpu.do_instr()) {
                // Handle timer interrupt
                match int {
                    // Interrupt at the start of the vblank period
                    IntType::VblankStart => {
                        clock.set_interrupt(IntType::VblankStart, render::VBLANK_PERIOD);
                        clock.set_interrupt(IntType::VblankEnd, render::VBLANK_DURATION);
                        cpu.interrupt(CpuInterrupt::Vblank);
                    }
                    // At the end, collect data from VRAM and render it
                    IntType::VblankEnd => {
                        break 'sim;
                    }
                }
            }
        }

        // Redraw screen
        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 0.0, 0.0);
        lcd.clear_viewport(&mut target, viewport, (1.0, 1.0, 1.0, 0.0));
        lcd.draw(&display, &mut target, viewport, cpu.get_ram());
        match target.finish().err() {
            Some(SwapBuffersError::ContextLost) => {
                panic!("OpenGL contetxt lost!");
            },
            Some(SwapBuffersError::AlreadySwapped) => {
                println!("Warning: OpenGL buffer already swapped");
            },
            None => (),
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::STDIN_FILENO;
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, ErrorKind, Write};

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct TermReset {
    orig_term: Termios
}

impl Drop for TermReset {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn enable_raw_mode() -> io::Result<TermReset> {
    let mut term = Termios::from_fd(STDIN_FILENO)?;
    let mode = TermReset { orig_term: term.clone() };

    term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    term.c_oflag &= !OPOST;
    term.c_cflag |= CS8;
    term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;

    tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
    Ok(mode)
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn clear_screen(stdout: &mut Stdout) -> io::Result<()> {
    stdout.write(b"\x1b[2J")?;
    stdout.write(b"\x1b[H")?;
    stdout.flush()
}

fn editor_refresh_screen(stdout: &mut Stdout) -> io::Result<()> {
    clear_screen(stdout)
}

fn editor_process_keypress(stdin: &mut Stdin) -> bool {
    let c = editor_read_key(stdin);

    match c {
        k if k == ctrl_key!(b'q') => {
            false
        },
        _ => true
    }
}

fn main() {
    let _reset = enable_raw_mode().unwrap();

    let mut stdin = io::stdin();
    let mut stdout = io::stdout();
    editor_refresh_screen(&mut stdout).unwrap();
    while editor_process_keypress(&mut stdin) {
        editor_refresh_screen(&mut stdout).unwrap();
    }
    clear_screen(&mut stdout).unwrap();
}
<commit_msg>Draw tildes<commit_after>extern crate libc;
extern crate termios;

use libc::STDIN_FILENO;
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, ErrorKind, Write};

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct TermReset {
    orig_term: Termios
}

impl Drop for TermReset {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn enable_raw_mode() -> io::Result<TermReset> {
    let mut term = Termios::from_fd(STDIN_FILENO)?;
    let mode = TermReset { orig_term: term.clone() };

    term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    term.c_oflag &= !OPOST;
    term.c_cflag |= CS8;
    term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
    term.c_cc[VMIN] = 0;
    term.c_cc[VTIME] = 1;

    tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
    Ok(mode)
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn clear_screen(stdout: &mut Stdout) -> io::Result<()> {
    stdout.write(b"\x1b[2J")?;
    stdout.write(b"\x1b[H")?;
    stdout.flush()
}

fn editor_draw_rows(stdout: &mut Stdout) -> io::Result<()> {
  for _ in 0..24 {
    stdout.write(b"~\r\n")?;
  }
  Ok(())
}

fn editor_refresh_screen(stdout: &mut Stdout) -> io::Result<()> {
    clear_screen(stdout)?;

    editor_draw_rows(stdout)?;

    stdout.write(b"\x1b[H")?;
    stdout.flush()
}

fn editor_process_keypress(stdin: &mut Stdin) -> bool {
    let c = editor_read_key(stdin);

    match c {
        k if k == ctrl_key!(b'q') => {
            false
        },
        _ => true
    }
}

fn main() {
    let _reset = enable_raw_mode().unwrap();

    let mut stdin = io::stdin();
    let mut stdout = io::stdout();
    editor_refresh_screen(&mut stdout).unwrap();
    while editor_process_keypress(&mut stdin) {
        editor_refresh_screen(&mut stdout).unwrap();
    }
    clear_screen(&mut stdout).unwrap();
}
<|endoftext|>


<commit_before>#![feature(deque_extras)]
#![feature(iter_arith)]
use std::fs::File;
use std::io::{BufReader, BufRead, Write};
use std::collections::{HashMap, VecDeque};

use std::env;
use std::io;

const WHITESPACE_FACTOR: isize = 5;
const WHITESPACE_REDUCE: isize = 2;
const CLASS_FACTOR: isize = 3;
const FIRST_FACTOR: isize = 3;
const CLASS_REDUCE: isize = 2;

const DIST_WEIGHT: isize = -10;
const HEAT_WEIGHT: isize = 5;
const LINE_REDUCE: isize = 50;

const MAX_LEN: usize = 80;

const MATCH_NUMBER: usize = 10;

#[derive(Debug)]
struct LineInfo {
    char_map: HashMap<char, Vec<usize>>,
    heatmap: Vec<isize>
}

#[derive(PartialEq)]
enum CharClass {
    Whitespace,
    Numeric,
    Alphabetic,
    First,
    Other
}

impl<T: AsRef<str>> From<T> for LineInfo {
    fn from(item: T) -> LineInfo {
        let mut map: HashMap<char, Vec<usize>> = HashMap::new();
        let mut heat = vec![];
        let line = item.as_ref();

        let mut ws_score = 0;
        let mut cs_score = 0;
        let mut cur_class = CharClass::First;
        // character class changes don't stack
        let mut cs_change = false;

        for (idx, c) in line.chars().enumerate() {
            // don't map whitespace
            if !c.is_whitespace() {
                // update the character class change score if needed
                if cur_class == CharClass::First {
                    // add the first character factor on top of class change
                    cs_score += FIRST_FACTOR;
                }
                if c.is_numeric() {
                    if cur_class != CharClass::Numeric {
                        cur_class = CharClass::Numeric;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else if c.is_alphabetic() {
                    if cur_class != CharClass::Alphabetic {
                        cur_class = CharClass::Alphabetic;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else {
                    if cur_class != CharClass::Other {
                        cur_class = CharClass::Other;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                }

                // add an entry in the character map
                map.entry(c).or_insert(Vec::new()).push(idx);
                if c.is_uppercase() {
                    for lc in c.to_lowercase() {
                        // also insert all lowercase equivalents of this character
                        // but not the other way around, so that typing something
                        // uppercase specifies to match uppercase
                        map.entry(lc).or_insert(Vec::new()).push(idx);
                    }
                }
            } else {
                // whitespace is treated differently
                cur_class = CharClass::Whitespace;
                ws_score = WHITESPACE_FACTOR;
            }

            // push to the heatmap
            heat.push(ws_score + cs_score);

            // reduce things
            ws_score /= WHITESPACE_REDUCE;
            if !cs_change {
                cs_score /= CLASS_REDUCE;
            }
        }

        LineInfo {
            char_map: map,
            heatmap: heat
        }
    }
}

impl LineInfo {
    fn query_sequence<T: AsRef<str>>(&self, query_item: T) -> Option<Vec<Vec<usize>>> {
        let query = query_item.as_ref();
        let mut positions: Vec<Vec<usize>> = vec![];

        for c in query.chars() {
            match self.char_map.get(&c) {
                None => break,
                Some(list) => {
                    let to_push;
                    match positions.last() {
                        None => {
                            to_push = list.clone();
                        },
                        Some(item) => {
                            match list.binary_search(&item[0]) {
                                Ok(idx) => {
                                    if idx >= list.len() - 1 {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx + 1).1.into();
                                    }
                                },
                                Err(idx) => {
                                    if idx >= list.len() {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx).1.into();
                                    }
                                }
                            }
                        }
                    }
                    positions.push(to_push);
                }
            }
        }

        if positions.len() == query.len() {
            Some(positions)
        } else {
            None
        }
    }

    fn query_positions<T: AsRef<str>>(&self, query: T) -> Option<Vec<Vec<usize>>> {
        match self.query_sequence(query) {
            None => None,
            Some(positions) => {
                // matching line
                // create our idx vector
                let mut idx = vec![0; positions.len()];
                let mut result = vec![];
                loop {
                    // check that current configuration is strictly increasing
                    let mut ignore = false;
                    {
                        let mut last_pos = None;
                        for (i, pos) in idx.iter().enumerate() {
                            match last_pos {
                                None => last_pos = Some(positions[i][*pos]),
                                Some(other) => {
                                    if other >= positions[i][*pos] {
                                        ignore = true;
                                        break;
                                    } else {
                                        last_pos = Some(positions[i][*pos]);
                                    }
                                }
                            }
                        }
                    }

                    if !ignore {
                        // add the configuration to the list
                        result.push(idx.iter().enumerate().map(|(i, pos)| {positions[i][*pos]}).collect());
                    }

                    // update our position vector
                    let mut update_idx = idx.len() - 1;
                    let mut finished = false;
                    loop {
                        idx[update_idx] += 1;
                        if idx[update_idx] >= positions[update_idx].len() {
                            if update_idx == 0 {
                                // we're finished with all permutations
                                finished = true;
                                break;
                            } else {
                                idx[update_idx] = 0;
                                update_idx -= 1;
                            }
                        } else {
                            // finished updating for this permutation
                            break;
                        }
                    }
                    if finished {
                        // finished with everything
                        break;
                    }
                }

                // return result
                Some(result)
            }
        }
    }

    fn query_score<T: AsRef<str>>(&self, query: T) -> Option<isize> {
        match self.query_positions(&query) {
            None => None,
            Some(positions) => {
                let mut top_score = None;
                for pgroup in positions.iter() {
                    // find the average distance between the indexes
                    let mut dist_total = 0;
                    let mut dist_count = 0;
                    for i in 0..pgroup.len() - 1 {
                        dist_total += (pgroup[i + 1] - pgroup[i]) as isize;
                        dist_count += 1;
                    }
                    // sum the heatmap
                    let heat_sum: isize = pgroup.iter().map(|pos| {self.heatmap[*pos]}).sum();
                    let score = (dist_total / dist_count) * DIST_WEIGHT +
                        heat_sum * HEAT_WEIGHT;
                    match top_score {
                        None => top_score = Some(score),
                        Some(last) => {
                            if score > last {
                                top_score = Some(score);
                            }
                        }
                    }
                }

                // return the result
                top_score
            }
        }
    }
}

fn main() {
    let history_path = match env::var("HISTFILE") {
        Ok(p) => p,
        Err(e) => panic!("Failed to get bash history file: {}", e)
    };

    let input_file = match File::open(&history_path) {
        Ok(f) => BufReader::new(f),
        Err(e) => panic!("Could not open history file: {}", e)
    };

    let mut query = String::new();

    print!("Match input: ");
    match io::stdout().flush() {
        Err(e) => panic!("Failed to flush stdout: {}", e),
        Ok(_) => {}
    }

    match io::stdin().read_line(&mut query) {
        Ok(_) => {},
        Err(e) => panic!("Failed to read input line: {}", e)
    }

    match query.pop() {
        Some('\n') => {/* pop off trailing newline */},
        Some(c) => query.push(c),
        None => {/* Do nothing with an empty query */}
    }

    let mut line_number = -1;

    let mut matches = VecDeque::with_capacity(MATCH_NUMBER + 1);

    for m_line in input_file.lines() {
        let line = match m_line {
            Ok(mut line) => {
                if line.len() > MAX_LEN {
                    let mut cut_at = None;
                    for (idx, _) in line.char_indices() {
                        if idx > MAX_LEN {
                            cut_at = Some(idx);
                            break;
                        }
                    }
                    match cut_at {
                        None => {
                            // do nothing, last character spans the 80th byte
                        },
                        Some(idx) => {
                            line.truncate(idx);
                        }
                    }
                }
                // return the result
                line
            },
            Err(e) => panic!("Failed to read line: {}", e)
        };

        let info = LineInfo::from(&line);
        line_number += 1;

        let line_score = match info.query_score(&query) {
            None => {
                // non-matching line
                continue;
            },
            Some(score) => {
                score + line_number / LINE_REDUCE
            }
        };

        //println!("Line {:?} score {}", &line, line_score);

        // push the match if it's better than the last ones
        match matches.front() {
            None => {
                matches.push_front((line_score, line));
            },
            Some(&(score, _)) => {
                if line_score >= score {
                    matches.push_front((line_score, line));
                }
            }
        }

        // truncate the list
        matches.truncate(MATCH_NUMBER);
    }

    println!("Matches:");

    for &(_, ref line) in matches.iter() {
        println!("{}", line);
    }
}
<commit_msg>Split history reading and query<commit_after>#![feature(deque_extras)]
#![feature(iter_arith)]
use std::fs::File;
use std::io::{BufReader, BufRead, Write};
use std::collections::{HashMap, VecDeque};

use std::env;
use std::io;

const WHITESPACE_FACTOR: isize = 5;
const WHITESPACE_REDUCE: isize = 2;
const CLASS_FACTOR: isize = 3;
const FIRST_FACTOR: isize = 3;
const CLASS_REDUCE: isize = 2;

const DIST_WEIGHT: isize = -10;
const HEAT_WEIGHT: isize = 5;
const FACTOR_REDUCE: isize = 50;

const MAX_LEN: usize = 80;

const MATCH_NUMBER: usize = 10;

#[derive(Debug)]
struct LineInfo {
    char_map: HashMap<char, Vec<usize>>,
    heatmap: Vec<isize>,
    pub factor: isize
}

#[derive(PartialEq)]
enum CharClass {
    Whitespace,
    Numeric,
    Alphabetic,
    First,
    Other
}

impl LineInfo {
    fn new<T: AsRef<str>>(item: T, factor: isize) -> LineInfo {
        let mut map: HashMap<char, Vec<usize>> = HashMap::new();
        let mut heat = vec![];
        let line = item.as_ref();

        let mut ws_score = 0;
        let mut cs_score = 0;
        let mut cur_class = CharClass::First;
        // character class changes don't stack
        let mut cs_change = false;

        for (idx, c) in line.chars().enumerate() {
            // don't map whitespace
            if !c.is_whitespace() {
                // update the character class change score if needed
                if cur_class == CharClass::First {
                    // add the first character factor on top of class change
                    cs_score += FIRST_FACTOR;
                }
                if c.is_numeric() {
                    if cur_class != CharClass::Numeric {
                        cur_class = CharClass::Numeric;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else if c.is_alphabetic() {
                    if cur_class != CharClass::Alphabetic {
                        cur_class = CharClass::Alphabetic;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                } else {
                    if cur_class != CharClass::Other {
                        cur_class = CharClass::Other;
                        if !cs_change {
                            cs_score += CLASS_FACTOR;
                            cs_change = true;
                        }
                    } else {
                        cs_change = false;
                    }
                }

                // add an entry in the character map
                map.entry(c).or_insert(Vec::new()).push(idx);
                if c.is_uppercase() {
                    for lc in c.to_lowercase() {
                        // also insert all lowercase equivalents of this character
                        // but not the other way around, so that typing something
                        // uppercase specifies to match uppercase
                        map.entry(lc).or_insert(Vec::new()).push(idx);
                    }
                }
            } else {
                // whitespace is treated differently
                cur_class = CharClass::Whitespace;
                ws_score = WHITESPACE_FACTOR;
            }

            // push to the heatmap
            heat.push(ws_score + cs_score);

            // reduce things
            ws_score /= WHITESPACE_REDUCE;
            if !cs_change {
                cs_score /= CLASS_REDUCE;
            }
        }

        LineInfo {
            char_map: map,
            heatmap: heat,
            factor: factor
        }
    }

    fn query_sequence<T: AsRef<str>>(&self, query_item: T) -> Option<Vec<Vec<usize>>> {
        let query = query_item.as_ref();
        let mut positions: Vec<Vec<usize>> = vec![];

        for c in query.chars() {
            match self.char_map.get(&c) {
                None => break,
                Some(list) => {
                    let to_push;
                    match positions.last() {
                        None => {
                            to_push = list.clone();
                        },
                        Some(item) => {
                            match list.binary_search(&item[0]) {
                                Ok(idx) => {
                                    if idx >= list.len() - 1 {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx + 1).1.into();
                                    }
                                },
                                Err(idx) => {
                                    if idx >= list.len() {
                                        // line is non-matching
                                        break;
                                    } else {
                                        to_push = list.split_at(idx).1.into();
                                    }
                                }
                            }
                        }
                    }
                    positions.push(to_push);
                }
            }
        }

        if positions.len() == query.len() {
            Some(positions)
        } else {
            None
        }
    }

    fn query_positions<T: AsRef<str>>(&self, query: T) -> Option<Vec<Vec<usize>>> {
        match self.query_sequence(query) {
            None => None,
            Some(positions) => {
                // matching line
                // create our idx vector
                let mut idx = vec![0; positions.len()];
                let mut result = vec![];
                loop {
                    // check that current configuration is strictly increasing
                    let mut ignore = false;
                    {
                        let mut last_pos = None;
                        for (i, pos) in idx.iter().enumerate() {
                            match last_pos {
                                None => last_pos = Some(positions[i][*pos]),
                                Some(other) => {
                                    if other >= positions[i][*pos] {
                                        ignore = true;
                                        break;
                                    } else {
                                        last_pos = Some(positions[i][*pos]);
                                    }
                                }
                            }
                        }
                    }

                    if !ignore {
                        // add the configuration to the list
                        result.push(idx.iter().enumerate().map(|(i, pos)| {positions[i][*pos]}).collect());
                    }

                    // update our position vector
                    let mut update_idx = idx.len() - 1;
                    let mut finished = false;
                    loop {
                        idx[update_idx] += 1;
                        if idx[update_idx] >= positions[update_idx].len() {
                            if update_idx == 0 {
                                // we're finished with all permutations
                                finished = true;
                                break;
                            } else {
                                idx[update_idx] = 0;
                                update_idx -= 1;
                            }
                        } else {
                            // finished updating for this permutation
                            break;
                        }
                    }
                    if finished {
                        // finished with everything
                        break;
                    }
                }

                // return result
                Some(result)
            }
        }
    }

    fn query_score<T: AsRef<str>>(&self, query: T) -> Option<isize> {
        match self.query_positions(query) {
            None => None,
            Some(positions) => {
                let mut top_score = None;
                for pgroup in positions.iter() {
                    // find the average distance between the indexes
                    let mut dist_total = 0;
                    let mut dist_count = 0;
                    for i in 0..pgroup.len() - 1 {
                        dist_total += (pgroup[i + 1] - pgroup[i]) as isize;
                        dist_count += 1;
                    }
                    // sum the heatmap
                    let heat_sum: isize = pgroup.iter().map(|pos| {self.heatmap[*pos]}).sum();
                    let score = (dist_total / dist_count) * DIST_WEIGHT +
                        heat_sum * HEAT_WEIGHT;
                    match top_score {
                        None => top_score = Some(score),
                        Some(last) => {
                            if score > last {
                                top_score = Some(score);
                            }
                        }
                    }
                }

                // return the result
                match top_score {
                    None => None,
                    Some(score) => {
                        Some(score + self.factor / FACTOR_REDUCE)
                    }
                }
            }
        }
    }
}

fn main() {
    let history_path = match env::var("HISTFILE") {
        Ok(p) => p,
        Err(e) => panic!("Failed to get bash history file: {}", e)
    };

    let input_file = match File::open(&history_path) {
        Ok(f) => BufReader::new(f),
        Err(e) => panic!("Could not open history file: {}", e)
    };

    let mut line_number = -1;

    // create a hashmap of lines to info
    let mut lines: HashMap<String, LineInfo> = HashMap::new();

    // read the history
    println!("Reading history...");
    for m_line in input_file.lines() {
        let line = match m_line {
            Ok(mut line) => {
                if line.len() > MAX_LEN {
                    let mut cut_at = None;
                    for (idx, _) in line.char_indices() {
                        if idx > MAX_LEN {
                            cut_at = Some(idx);
                            break;
                        }
                    }
                    match cut_at {
                        None => {
                            // do nothing, last character spans the 80th byte
                        },
                        Some(idx) => {
                            line.truncate(idx);
                        }
                    }
                }
                // return the result
                line
            },
            Err(e) => panic!("Failed to read line: {}", e)
        };

        line_number += 1;

        // generate the line info
        let info = LineInfo::new(&line, line_number);

        // insert the line into the map
        lines.insert(line, info);
    }

    let mut query = String::new();

    print!("Match input: ");
    match io::stdout().flush() {
        Err(e) => panic!("Failed to flush stdout: {}", e),
        Ok(_) => {}
    }

    match io::stdin().read_line(&mut query) {
        Ok(_) => {},
        Err(e) => panic!("Failed to read input line: {}", e)
    }

    match query.pop() {
        Some('\n') => {/* pop off trailing newline */},
        Some(c) => query.push(c),
        None => {/* Do nothing with an empty query */}
    }

    // allocate plus one element since we trim after adding one too many
    let mut matches = VecDeque::with_capacity(MATCH_NUMBER + 1);

    // search for a match
    for (line, info) in lines.iter() {
        let line_score = match info.query_score(&query) {
            None => {
                // non-matching line
                continue;
            },
            Some(score) => {
                score
            }
        };

        // push the match if it's better than the last ones
        match matches.front() {
            None => {
                matches.push_front((line_score, info.factor, line));
            },
            Some(&(score, factor, _)) => {
                if line_score > score || (line_score == score && info.factor > factor) {
                    matches.push_front((line_score, info.factor, line));
                }
            }
        }

        // truncate the list
        matches.truncate(MATCH_NUMBER);
    }

    println!("Matches:");

    for &(_, _, ref line) in matches.iter() {
        println!("{}", line);
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(regex_macros)]

extern crate rustc_serialize;
extern crate websocket;
extern crate hyper;
extern crate image;
extern crate qrcode;
extern crate regex;

pub mod blockchain_info;
pub mod payment_detection;
pub mod server;
pub mod qr;

use hyper::server::Server;

use server::{routes};

fn main() {
    let server = Server::http("127.0.0.1:1337").unwrap();
    let _guard = server.handle(routes);
    println!("Listening on http://127.0.0.1:1337");
}
<commit_msg>get ip and port from command line arguments<commit_after>#![feature(plugin)]
#![plugin(regex_macros)]

extern crate rustc_serialize;
extern crate websocket;
extern crate hyper;
extern crate image;
extern crate qrcode;
extern crate regex;

pub mod blockchain_info;
pub mod payment_detection;
pub mod server;
pub mod qr;

use std::env;

use hyper::server::Server;

use server::{routes};

fn main() {
	let mut ip = env::args().nth(1).unwrap();
	ip.push_str(":");
	ip.push_str(&*env::args().nth(2).unwrap());

    let server = Server::http(&*ip).unwrap();
    let _guard = server.handle(routes);

    println!("Listening on http://{}", ip);
}
<|endoftext|>


<commit_before>extern crate getopts;
extern crate openby;
use std::env;
use std::io;
use std::io::Write;
use std::path;
use std::process;
use getopts::Options;
use getopts::ParsingStyle;

use openby::error;
use openby::config;

const DEFAULT_CONF_PATH: &str = "~/.config/openby/config";

fn main() {
    let exit_code = match run() {
        Ok(_) => 0,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}", e).unwrap();
            1
        }
    };
    process::exit(exit_code);
}

fn run() -> Result<(), error::AppError> {
    let args: Vec<String> = env::args().collect();
    let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
    let (file_name, conf_name) = parse_options(&args_str)?;
    println!("Ok: {} {}", file_name, conf_name);

    open_by(&conf_name, &file_name)?;

    Ok(())
}

fn parse_options(args: &[&str]) -> Result<(String, String), error::AppError> {
    let program = &(&args[0]);

    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::FloatingFrees);
    opts.optopt("c", "config", "specify configure file name", "CONFIG");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(&args[1..]).unwrap();

    if matches.opt_present("h") {
        print_usage(program, &opts);
        process::exit(0);
    }

    let file_name = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(program, &opts);
        return Result::Err(error::AppError::Io(
            io::Error::new(io::ErrorKind::NotFound, "FILE is not found"),
        ));
    };

    let conf_name = if let Some(c) = matches.opt_str("c") {
        c
    } else {
        DEFAULT_CONF_PATH.to_string()
    };

    Ok((file_name, conf_name))
}

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options] FILE", program);
    println!("{}", opts.usage(&brief));
}

fn open_by(conf_name: &str, file_name: &str) -> Result<(), error::AppError> {
    let mut conf = config::Config::load(&conf_name)?;

    let file_path = path::Path::new(file_name);
    if !file_path.exists() {
        return Result::Err(error::AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", file_path.to_str().unwrap_or("")),
        )));
    }

    let ext = file_path.extension().ok_or_else(|| {
        error::AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!(
                "{} has not extension",
                file_path.to_str().unwrap_or("")
            ),
        ))
    })?;

    let ext_str = ext.to_str().unwrap();
    match conf.get_commnad(ext_str) {
        Some(cmdline) => {
            let cmds: Vec<&str> = cmdline.split_whitespace().collect();
            let (cmd, option) = cmds.split_first().unwrap();
            println!("exec {:?} {:?} {:?}", cmd, option, file_path);

            process::Command::new(cmd)
                .args(option.into_iter())
                .arg(file_path)
                .status()
                .expect("failed to run");
        }
        None => {
            println!("set the command associated with .{}", ext_str);
            print!("> ");
            let reader = io::stdin();
            let cmdline = input_command(reader.lock())?;
            let _ = conf.add(cmdline.as_str(), ext_str);
            println!("{:?}", conf);
        }
    }

    Ok(())
}

fn input_command<T: io::BufRead>(mut reader: T) -> Result<String, error::AppError> {
    let _ = std::io::stdout().flush();
    let mut input = String::new();
    let _ = reader.read_line(&mut input).map_err(error::AppError::Io)?;
    let concated: String = input.split_whitespace().fold(String::new(), |mut s, w| {
        if s.len() > 0 {
            s.push(' ');
        }
        s.push_str(&w);
        s
    });
    Ok(concated)
}

#[test]
fn test_parse_options() {
    let no_arguments = vec!["openby", "input.file", "-c", "configure.file"];

    assert!(parse_options(&no_arguments[0..1]).is_err());
    assert_eq!(
        ("input.file".to_string(), DEFAULT_CONF_PATH.to_string()),
        parse_options(&no_arguments[0..2]).unwrap_or(("".to_string(), "".to_string()))
    );
    assert_eq!(
        ("input.file".to_string(), "configure.file".to_string()),
        parse_options(&no_arguments).unwrap_or(("".to_string(), "".to_string()))
    );
}
<commit_msg>create/update config file<commit_after>extern crate getopts;
extern crate openby;
use std::env;
use std::io;
use std::io::Write;
use std::path;
use std::process;
use getopts::Options;
use getopts::ParsingStyle;

use openby::error;
use openby::config;

const DEFAULT_CONF_PATH: &str = "~/.config/openby/config";

fn main() {
    let exit_code = match run() {
        Ok(_) => 0,
        Err(e) => {
            writeln!(&mut io::stderr(), "{:?}", e).unwrap();
            1
        }
    };
    process::exit(exit_code);
}

fn run() -> Result<(), error::AppError> {
    let args: Vec<String> = env::args().collect();
    let args_str: Vec<&str> = args.iter().map(|s| s.as_str()).collect();
    let (file_name, conf_name) = parse_options(&args_str)?;
    println!("Ok: {} {}", file_name, conf_name);

    open_by(&conf_name, &file_name)?;

    Ok(())
}

fn parse_options(args: &[&str]) -> Result<(String, String), error::AppError> {
    let program = &(&args[0]);

    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::FloatingFrees);
    opts.optopt("c", "config", "specify configure file name", "CONFIG");
    opts.optflag("h", "help", "print this help menu");

    let matches = opts.parse(&args[1..]).unwrap();

    if matches.opt_present("h") {
        print_usage(program, &opts);
        process::exit(0);
    }

    let file_name = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(program, &opts);
        return Result::Err(error::AppError::Io(
            io::Error::new(io::ErrorKind::NotFound, "FILE is not found"),
        ));
    };

    let conf_name = if let Some(c) = matches.opt_str("c") {
        c
    } else {
        DEFAULT_CONF_PATH.to_string()
    };

    Ok((file_name, conf_name))
}

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options] FILE", program);
    println!("{}", opts.usage(&brief));
}

fn open_by(conf_name: &str, file_name: &str) -> Result<(), error::AppError> {
    //let mut conf = config::Config::load(&conf_name)?;
    let mut conf = if path::Path::new(conf_name).exists() {
        config::Config::load(&conf_name)?
    } else {
        config::Config::new()
    };

    let file_path = path::Path::new(file_name);
    if !file_path.exists() {
        return Result::Err(error::AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!("{} is not exist", file_path.to_str().unwrap_or("")),
        )));
    }

    let ext = file_path.extension().ok_or_else(|| {
        error::AppError::Io(io::Error::new(
            io::ErrorKind::NotFound,
            format!(
                "{} has not extension",
                file_path.to_str().unwrap_or("")
            ),
        ))
    })?;

    let ext_str = ext.to_str().unwrap();
    let cmdline = conf.get_commnad(ext_str).unwrap_or_else(|| {
        println!("set the command associated with .{}", ext_str);
        print!("> ");
        let reader = io::stdin();
        let readline = input_command(reader.lock()).unwrap();
        let _ = conf.add(readline.as_str(), ext_str);
        println!("{:?}", conf);
        readline
    });
    let cmds: Vec<&str> = cmdline.split_whitespace().collect();
    let (cmd, option) = cmds.split_first().unwrap();
    println!("exec {:?} {:?} {:?}", cmd, option, file_path);
    process::Command::new(cmd)
        .args(option.into_iter())
        .arg(file_path)
        .status()
        .expect("failed to run");

    conf.save(conf_name)?;

    Ok(())
}

fn input_command<T: io::BufRead>(mut reader: T) -> Result<String, error::AppError> {
    let _ = std::io::stdout().flush();
    let mut input = String::new();
    let _ = reader.read_line(&mut input).map_err(error::AppError::Io)?;
    let concated: String = input.split_whitespace().fold(String::new(), |mut s, w| {
        if s.len() > 0 {
            s.push(' ');
        }
        s.push_str(&w);
        s
    });
    Ok(concated)
}

#[test]
fn test_parse_options() {
    let no_arguments = vec!["openby", "input.file", "-c", "configure.file"];

    assert!(parse_options(&no_arguments[0..1]).is_err());
    assert_eq!(
        ("input.file".to_string(), DEFAULT_CONF_PATH.to_string()),
        parse_options(&no_arguments[0..2]).unwrap_or(("".to_string(), "".to_string()))
    );
    assert_eq!(
        ("input.file".to_string(), "configure.file".to_string()),
        parse_options(&no_arguments).unwrap_or(("".to_string(), "".to_string()))
    );
}
<|endoftext|>


<commit_before>extern crate html5ever;
#[macro_use(qualname,ns,atom)]
extern crate string_cache;
#[macro_use(format_tendril)]
extern crate tendril;

use html5ever::rcdom::NodeEnum::Element;
use html5ever::rcdom::{RcDom, Handle};
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::{TreeBuilderOpts, TreeSink, NodeOrText};
use html5ever::{ParseOpts, parse_document};
use std::string::String;
use std::io;

enum Entry {
    Const(Handle),
    // Enum(Handle),
    // Function(Handle),
    // Macro(Handle),
    Method(Handle),
    Module(Handle),
    Struct(Handle),
    Trait(Handle),
    Type(Handle),
}

fn main() {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
        ..Default::default()
    };

    let stdin = io::stdin();
    let mut dom = parse_document(RcDom::default(), opts)
                      .from_utf8()
                      .read_from(&mut stdin.lock())
                      .unwrap();

    let entries = find_entry_elements(&mut dom);

    // https://kapeli.com/docsets#tableofcontents
    // https://kapeli.com/docsets#supportedentrytypes
    for entry in entries {
        match entry {
            Entry::Method(e) => {
                add_dash_link_before_entry(&mut dom, &e, "method", "42")
            }
            _ => {}
        }
    }

    let mut bytes = vec![];
    serialize(&mut bytes, &dom.document, SerializeOpts::default()).unwrap();
    let result = String::from_utf8(bytes).unwrap();
    println!("{}", result);
}

fn add_dash_link_before_entry(dom: &mut RcDom, p: &Handle, entrytype: &str, entryname: &str) {
    let class_attr = html5ever::Attribute {
        name: qualname!("", "class"),
        value: format_tendril!("dashAnchor"),
    };

    let name_attr = html5ever::Attribute {
        name: qualname!("", "name"),
        // TODO: percent escape entryname
        value: format_tendril!("//apple_ref/cpp/{}/{}", entrytype, entryname),
    };

    let dash_link = dom.create_element(qualname!(html, "a"), vec![name_attr, class_attr.clone()]);
    let _ = dom.append_before_sibling(p.clone(), NodeOrText::AppendNode(dash_link));
}

fn find_entry_elements(dom: &mut RcDom) -> Vec<Entry> {
    let mut entries = vec![];
    walk_tree(&dom.document, &mut entries);
    entries
}

fn walk_tree(h: &Handle, entries: &mut Vec<Entry>) {
    let node = h.borrow();
    for e in node.children.iter() {
        if let Element(ref name, _, ref attrs) = e.borrow().node {
            if name.eq(&qualname!(html, "section")) {
                let tag = &(*name.local.to_ascii_lowercase());
                if let Some(attr) = attrs.iter().find(|ref x| x.name == qualname!("", "class")) {
                    match (tag, attr.clone().value.to_string().as_str()) {
                        ("h4", "method") => entries.push(Entry::Method(e.clone())),
                        ("section", "content mod") => entries.push(Entry::Module(e.clone())),
                        ("section", "content constant") => entries.push(Entry::Const(e.clone())),
                        ("section", "content struct") => entries.push(Entry::Struct(e.clone())),
                        ("section", "content trait") => entries.push(Entry::Trait(e.clone())),
                        ("section", "type") => entries.push(Entry::Type(e.clone())),
                        (_, _) => {}
                    }
                }
            }
        }
        walk_tree(e, entries);
    }
}

#[cfg(test)]
mod tests {
    use html5ever::{ParseOpts, parse_document};
    use html5ever::rcdom::{RcDom, Handle};
    use tendril::TendrilSink;
    use html5ever::tree_builder::TreeBuilderOpts;

    fn dom_from_snippet(s: &str) -> Handle {
        let opts = ParseOpts {
            tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
            ..Default::default()
        };

        let dom = parse_document(RcDom::default(), opts)
                      .from_utf8()
                      .read_from(&mut format!("{}", s)
                                          .as_bytes())
                      .unwrap();

        dom.document
    }

    #[test]
    fn it_extracts_name_for_const_correctly() {
        let document_with_constant_section = dom_from_snippet("

alloc::\
                                                               <wbr>boxed::<wbr>HEAP  \
                                                               [−] \
                                                                [src]
</sect\
                                                               ion>");

        let mut entries: Vec<super::Entry> = Vec::new();
        super::walk_tree(&document_with_constant_section, &mut entries);

        assert_eq!(entries.len(), 1);
        match entries[0] {
            super::Entry::Const(_) => assert!(true),
            _ => assert!(false),
        }
    }
}

// const:

// enum:

Enum collections::<wbr>borrow::<wbr>Cow1.0.0  [−]  [src]

// function:

Function std::<wbr>fs::<wbr>metadata1.0.0  [−]  [src]

// macro:
 //
std::<wbr>println!1.0.0  [−]  [src]

// method:
fn hash<H: Hasher>(&self, state: &mut H)[−]

// modules:

Crate collections  [−]  [src]

// struct:

Struct std::<wbr>io::<wbr>Bytes1.0.0  [−]  [src]

// trait:

Trait collections::<wbr>fmt::<wbr>Binary1.0.0  [−]  [src]

// type:
type Output = bool

<commit_msg>Implement constant name extraction<commit_after>extern crate html5ever;
#[macro_use(qualname,ns,atom)]
extern crate string_cache;
#[macro_use(format_tendril)]
extern crate tendril;

use html5ever::rcdom::NodeEnum::{Element, Text};
use html5ever::rcdom::{Handle, RcDom};
use html5ever::serialize::{SerializeOpts, serialize};
use html5ever::tendril::TendrilSink;
use html5ever::tree_builder::{NodeOrText, TreeBuilderOpts, TreeSink};
use html5ever::{ParseOpts, parse_document};

use std::io;
use std::string::String;

enum Entry {
    Const(Handle),
    // Enum(Handle),
    // Function(Handle),
    // Macro(Handle),
    Method(Handle),
    Module(Handle),
    Struct(Handle),
    Trait(Handle),
    Type(Handle),
}

fn main() {
    let opts = ParseOpts {
        tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
        ..Default::default()
    };

    let stdin = io::stdin();
    let mut dom = parse_document(RcDom::default(), opts)
                      .from_utf8()
                      .read_from(&mut stdin.lock())
                      .unwrap();

    let entries = find_entry_elements(&mut dom);

    // https://kapeli.com/docsets#tableofcontents
    // https://kapeli.com/docsets#supportedentrytypes
    for entry in entries {
        match entry {
            Entry::Method(e) => {
                add_dash_link_before_entry(&mut dom, &e, "method", "42")
            }
            _ => {}
        }
    }

    let mut bytes = vec![];
    serialize(&mut bytes, &dom.document, SerializeOpts::default()).unwrap();
    let result = String::from_utf8(bytes).unwrap();
    println!("{}", result);
}

fn add_dash_link_before_entry(dom: &mut RcDom, p: &Handle, entrytype: &str, entryname: &str) {
    let class_attr = html5ever::Attribute {
        name: qualname!("", "class"),
        value: format_tendril!("dashAnchor"),
    };

    let name_attr = html5ever::Attribute {
        name: qualname!("", "name"),
        // TODO: percent escape entryname
        value: format_tendril!("//apple_ref/cpp/{}/{}", entrytype, entryname),
    };

    let dash_link = dom.create_element(qualname!(html, "a"), vec![name_attr, class_attr.clone()]);
    let _ = dom.append_before_sibling(p.clone(), NodeOrText::AppendNode(dash_link));
}

fn find_entry_elements(dom: &mut RcDom) -> Vec<Entry> {
    let mut entries = vec![];
    walk_tree(&dom.document, &mut entries);
    entries
}

fn walk_tree(h: &Handle, entries: &mut Vec<Entry>) {
    let node = h.borrow();
    for e in node.children.iter() {
        if let Element(ref name, _, ref attrs) = e.borrow().node {
            let tag = &(*name.local.to_ascii_lowercase());
            if let Some(attr) = attrs.iter().find(|ref x| x.name == qualname!("", "class")) {
                match (tag, attr.clone().value.to_string().as_str()) {
                    ("h4", "method") => entries.push(Entry::Method(e.clone())),
                    ("section", "content mod") => entries.push(Entry::Module(e.clone())),
                    ("section", "content constant") => entries.push(Entry::Const(e.clone())),
                    ("section", "content struct") => entries.push(Entry::Struct(e.clone())),
                    ("section", "content trait") => entries.push(Entry::Trait(e.clone())),
                    ("section", "type") => entries.push(Entry::Type(e.clone())),
                    (_, _) => {}
                }
            }
        }
        walk_tree(e, entries);
    }
}

fn find_element_with_class(h: &Handle, c: &str) -> Option<Handle> {
    let node = h.borrow();
    for e in node.children.iter() {
        if let Element(_, _, ref attrs) = e.borrow().node {
            if let Some(attr) = attrs.iter().find(|ref x| x.name == qualname!("", "class")) {
                if attr.clone().value.to_string() == c {
                    return Some(e.clone());
                }
            }
        }
        if let Some(e) = find_element_with_class(e, c) {
            return Some(e);
        }
    }
    return None;
}

fn extract_entry_name(e: &Entry, class: &str) -> Option<String> {
    match *e {
        Entry::Const(ref c) => {
            if let Some(e) = find_element_with_class(c, class) {
                let node = e.borrow();
                for e in node.children.iter() {
                    if let Text(ref t) = e.borrow().node {
                        return Some(t.to_string());
                    }
                }
            }
        }
        _ => (),
    }
    None
}

#[cfg(test)]
mod tests {
    use html5ever::{ParseOpts, parse_document};
    use html5ever::rcdom::{Handle, RcDom};
    use tendril::TendrilSink;
    use html5ever::tree_builder::TreeBuilderOpts;

    fn dom_from_snippet(s: &str) -> Handle {
        let opts = ParseOpts {
            tree_builder: TreeBuilderOpts { drop_doctype: true, ..Default::default() },
            ..Default::default()
        };

        let dom = parse_document(RcDom::default(), opts)
                      .from_utf8()
                      .read_from(&mut format!("{}", s)
                                          .as_bytes())
                      .unwrap();

        dom.document
    }

    #[test]
    fn it_extracts_name_for_const_correctly() {
        // alloc::boxed::HEAP constant, entry name: HEAP
        let document_with_constant_section = dom_from_snippet("

alloc::<wbr>boxed::<wbr>HEAP  [−]  [src]

");

        let mut entries: Vec<super::Entry> = Vec::new();
        super::walk_tree(&document_with_constant_section, &mut entries);

        assert_eq!(entries.len(), 1);
        match entries[0] {
            super::Entry::Const(_) => assert!(true),
            _ => assert!(false),
        }

        match super::extract_entry_name(&entries[0], "constant") {
            Some(s) => assert_eq!(s, *"HEAP".to_string()),
            None => assert_eq!(true, false),
        }
    }
}

// enum:

Enum collections::<wbr>borrow::<wbr>Cow1.0.0  [−]  [src]

// function:

Function std::<wbr>fs::<wbr>metadata1.0.0  [−]  [src]

// macro:
 //
std::<wbr>println!1.0.0  [−]  [src]

// method:
fn hash<H: Hasher>(&self, state: &mut H)[−]

// modules:

Crate collections  [−]  [src]

// struct:

Struct std::<wbr>io::<wbr>Bytes1.0.0  [−]  [src]

// trait:

Trait collections::<wbr>fmt::<wbr>Binary1.0.0  [−]  [src]

// type:
type Output = bool

<|endoftext|>