<commit_before>use rustc_data_structures::fx::FxHashSet;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::Path;

use rustc_errors::Handler;

#[cfg(test)]
mod tests;

macro_rules! try_something {
    ($e:expr, $diag:expr, $out:expr) => {{
        match $e {
            Ok(c) => c,
            Err(e) => {
                $diag.struct_err(&e.to_string()).emit();
                return $out;
            }
        }
    }};
}

#[derive(Debug, Clone, Eq)]
pub struct CssPath {
    pub name: String,
    pub children: FxHashSet<CssPath>,
}

// This PartialEq implementation IS NOT COMMUTATIVE!!!
//
// The order is very important: the second object must have all first's rules.
// However, the first doesn't require to have all second's rules.
impl PartialEq for CssPath {
    fn eq(&self, other: &CssPath) -> bool {
        if self.name != other.name {
            false
        } else {
            for child in &self.children {
                if !other.children.iter().any(|c| child == c) {
                    return false;
                }
            }
            true
        }
    }
}

impl Hash for CssPath {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        for x in &self.children {
            x.hash(state);
        }
    }
}

impl CssPath {
    fn new(name: String) -> CssPath {
        CssPath { name, children: FxHashSet::default() }
    }
}

/// All variants contain the position they occur.
#[derive(Debug, Clone, Copy)]
enum Events {
    StartLineComment(usize),
    StartComment(usize),
    EndComment(usize),
    InBlock(usize),
    OutBlock(usize),
}

impl Events {
    fn get_pos(&self) -> usize {
        match *self {
            Events::StartLineComment(p)
            | Events::StartComment(p)
            | Events::EndComment(p)
            | Events::InBlock(p)
            | Events::OutBlock(p) => p,
        }
    }

    fn is_comment(&self) -> bool {
        match *self {
            Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_) => true,
            _ => false,
        }
    }
}

fn previous_is_line_comment(events: &[Events]) -> bool {
    if let Some(&Events::StartLineComment(_)) = events.last() { true } else { false }
}

fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
    if let Some(&Events::StartComment(_)) = events.last() {
        return false;
    }
    v[pos + 1] == b'/'
}

fn load_css_events(v: &[u8]) -> Vec<Events> {
    let mut pos = 0;
    let mut events = Vec::with_capacity(100);

    while pos + 1 < v.len() {
        match v[pos] {
            b'/' if v[pos + 1] == b'*' => {
                events.push(Events::StartComment(pos));
                pos += 1;
            }
            b'/' if is_line_comment(pos, v, &events) => {
                events.push(Events::StartLineComment(pos));
                pos += 1;
            }
            b'\n' if previous_is_line_comment(&events) => {
                events.push(Events::EndComment(pos));
            }
            b'*' if v[pos + 1] == b'/' => {
                events.push(Events::EndComment(pos + 2));
                pos += 1;
            }
            b'{' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue;
                }
                events.push(Events::InBlock(pos + 1));
            }
            b'}' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue;
                }
                events.push(Events::OutBlock(pos + 1));
            }
            _ => {}
        }
        pos += 1;
    }
    events
}

fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
    while *pos < events.len() {
        if !events[*pos].is_comment() {
            return Some(events[*pos]);
        }
        *pos += 1;
    }
    None
}

fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
    let mut ret = Vec::with_capacity(3);

    ret.push(events[pos].get_pos());
    if pos > 0 {
        pos -= 1;
    }
    loop {
        if pos < 1 || !events[pos].is_comment() {
            let x = events[pos].get_pos();
            if *ret.last().unwrap() != x {
                ret.push(x);
            } else {
                ret.push(0);
            }
            break;
        }
        ret.push(events[pos].get_pos());
        pos -= 1;
    }
    if ret.len() & 1 != 0 && events[pos].is_comment() {
        ret.push(0);
    }
    ret.iter().rev().cloned().collect()
}

fn build_rule(v: &[u8], positions: &[usize]) -> String {
    minifier::css::minify(
        &positions
            .chunks(2)
            .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or(""))
            .collect::<String>()
            .trim()
            .replace("\n", " ")
            .replace("/", "")
            .replace("\t", " ")
            .replace("{", "")
            .replace("}", "")
            .split(' ')
            .filter(|s| !s.is_empty())
            .collect::<Vec<&str>>()
            .join(" "),
    )
    .unwrap_or_else(|_| String::new())
}

fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet<CssPath> {
    let mut paths = Vec::with_capacity(50);

    while *pos < events.len() {
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
            break;
        }
        if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
            *pos += 1;
        }
        while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            if let Some(ref mut path) = paths.last_mut() {
                for entry in inner(v, events, pos).iter() {
                    path.children.insert(entry.clone());
                }
            }
        }
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
        }
    }
    paths.iter().cloned().collect()
}

pub fn load_css_paths(v: &[u8]) -> CssPath {
    let events = load_css_events(v);
    let mut pos = 0;

    let mut parent = CssPath::new("parent".to_owned());
    parent.children = inner(v, &events, &mut pos);
    parent
}

pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
    if against.name == other.name {
        for child in &against.children {
            let mut found = false;
            let mut found_working = false;
            let mut tmp = Vec::new();

            for other_child in &other.children {
                if child.name == other_child.name {
                    if child != other_child {
                        get_differences(child, other_child, &mut tmp);
                    } else {
                        found_working = true;
                    }
                    found = true;
                    break;
                }
            }
            if !found {
                v.push(format!("  Missing \"{}\" rule", child.name));
            } else if !found_working {
                v.extend(tmp.iter().cloned());
            }
        }
    }
}

pub fn test_theme_against<P: AsRef<Path>>(
    f: &P,
    against: &CssPath,
    diag: &Handler,
) -> (bool, Vec<String>) {
    let data = try_something!(fs::read(f), diag, (false, vec![]));

    let paths = load_css_paths(&data);
    let mut ret = vec![];
    get_differences(against, &paths, &mut ret);
    (true, ret)
}
<commit_msg>Rollup merge of #70706 - gizmondo:check-theme, r=GuillaumeGomez<commit_after>use rustc_data_structures::fx::FxHashSet;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::Path;

use rustc_errors::Handler;

#[cfg(test)]
mod tests;

#[derive(Debug, Clone, Eq)]
pub struct CssPath {
    pub name: String,
    pub children: FxHashSet<CssPath>,
}

// This PartialEq implementation IS NOT COMMUTATIVE!!!
//
// The order is very important: the second object must have all first's rules.
// However, the first doesn't require to have all second's rules.
impl PartialEq for CssPath {
    fn eq(&self, other: &CssPath) -> bool {
        if self.name != other.name {
            false
        } else {
            for child in &self.children {
                if !other.children.iter().any(|c| child == c) {
                    return false;
                }
            }
            true
        }
    }
}

impl Hash for CssPath {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.name.hash(state);
        for x in &self.children {
            x.hash(state);
        }
    }
}

impl CssPath {
    fn new(name: String) -> CssPath {
        CssPath { name, children: FxHashSet::default() }
    }
}

/// All variants contain the position they occur.
#[derive(Debug, Clone, Copy)]
enum Events {
    StartLineComment(usize),
    StartComment(usize),
    EndComment(usize),
    InBlock(usize),
    OutBlock(usize),
}

impl Events {
    fn get_pos(&self) -> usize {
        match *self {
            Events::StartLineComment(p)
            | Events::StartComment(p)
            | Events::EndComment(p)
            | Events::InBlock(p)
            | Events::OutBlock(p) => p,
        }
    }

    fn is_comment(&self) -> bool {
        match *self {
            Events::StartLineComment(_) | Events::StartComment(_) | Events::EndComment(_) => true,
            _ => false,
        }
    }
}

fn previous_is_line_comment(events: &[Events]) -> bool {
    if let Some(&Events::StartLineComment(_)) = events.last() { true } else { false }
}

fn is_line_comment(pos: usize, v: &[u8], events: &[Events]) -> bool {
    if let Some(&Events::StartComment(_)) = events.last() {
        return false;
    }
    v[pos + 1] == b'/'
}

fn load_css_events(v: &[u8]) -> Vec<Events> {
    let mut pos = 0;
    let mut events = Vec::with_capacity(100);

    while pos + 1 < v.len() {
        match v[pos] {
            b'/' if v[pos + 1] == b'*' => {
                events.push(Events::StartComment(pos));
                pos += 1;
            }
            b'/' if is_line_comment(pos, v, &events) => {
                events.push(Events::StartLineComment(pos));
                pos += 1;
            }
            b'\n' if previous_is_line_comment(&events) => {
                events.push(Events::EndComment(pos));
            }
            b'*' if v[pos + 1] == b'/' => {
                events.push(Events::EndComment(pos + 2));
                pos += 1;
            }
            b'{' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue;
                }
                events.push(Events::InBlock(pos + 1));
            }
            b'}' if !previous_is_line_comment(&events) => {
                if let Some(&Events::StartComment(_)) = events.last() {
                    pos += 1;
                    continue;
                }
                events.push(Events::OutBlock(pos + 1));
            }
            _ => {}
        }
        pos += 1;
    }
    events
}

fn get_useful_next(events: &[Events], pos: &mut usize) -> Option<Events> {
    while *pos < events.len() {
        if !events[*pos].is_comment() {
            return Some(events[*pos]);
        }
        *pos += 1;
    }
    None
}

fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec<usize> {
    let mut ret = Vec::with_capacity(3);

    ret.push(events[pos].get_pos());
    if pos > 0 {
        pos -= 1;
    }
    loop {
        if pos < 1 || !events[pos].is_comment() {
            let x = events[pos].get_pos();
            if *ret.last().unwrap() != x {
                ret.push(x);
            } else {
                ret.push(0);
            }
            break;
        }
        ret.push(events[pos].get_pos());
        pos -= 1;
    }
    if ret.len() & 1 != 0 && events[pos].is_comment() {
        ret.push(0);
    }
    ret.iter().rev().cloned().collect()
}

fn build_rule(v: &[u8], positions: &[usize]) -> String {
    minifier::css::minify(
        &positions
            .chunks(2)
            .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or(""))
            .collect::<String>()
            .trim()
            .replace("\n", " ")
            .replace("/", "")
            .replace("\t", " ")
            .replace("{", "")
            .replace("}", "")
            .split(' ')
            .filter(|s| !s.is_empty())
            .collect::<Vec<&str>>()
            .join(" "),
    )
    .unwrap_or_else(|_| String::new())
}

fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet<CssPath> {
    let mut paths = Vec::with_capacity(50);

    while *pos < events.len() {
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
            break;
        }
        if let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            paths.push(CssPath::new(build_rule(v, &get_previous_positions(events, *pos))));
            *pos += 1;
        }
        while let Some(Events::InBlock(_)) = get_useful_next(events, pos) {
            if let Some(ref mut path) = paths.last_mut() {
                for entry in inner(v, events, pos).iter() {
                    path.children.insert(entry.clone());
                }
            }
        }
        if let Some(Events::OutBlock(_)) = get_useful_next(events, pos) {
            *pos += 1;
        }
    }
    paths.iter().cloned().collect()
}

pub fn load_css_paths(v: &[u8]) -> CssPath {
    let events = load_css_events(v);
    let mut pos = 0;

    let mut parent = CssPath::new("parent".to_owned());
    parent.children = inner(v, &events, &mut pos);
    parent
}

pub fn get_differences(against: &CssPath, other: &CssPath, v: &mut Vec<String>) {
    if against.name == other.name {
        for child in &against.children {
            let mut found = false;
            let mut found_working = false;
            let mut tmp = Vec::new();

            for other_child in &other.children {
                if child.name == other_child.name {
                    if child != other_child {
                        get_differences(child, other_child, &mut tmp);
                    } else {
                        found_working = true;
                    }
                    found = true;
                    break;
                }
            }
            if !found {
                v.push(format!("  Missing \"{}\" rule", child.name));
            } else if !found_working {
                v.extend(tmp.iter().cloned());
            }
        }
    }
}

pub fn test_theme_against<P: AsRef<Path>>(
    f: &P,
    against: &CssPath,
    diag: &Handler,
) -> (bool, Vec<String>) {
    let data = match fs::read(f) {
        Ok(c) => c,
        Err(e) => {
            diag.struct_err(&e.to_string()).emit();
            return (false, vec![]);
        }
    };

    let paths = load_css_paths(&data);
    let mut ret = vec![];
    get_differences(against, &paths, &mut ret);
    (true, ret)
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use json;
use sha1;
use serialize::{Encoder, Encodable, Decoder, Decodable};

use core::either::{Either, Left, Right};
use core::io;
use core::option;
use core::pipes::{recv, oneshot, PortOne, send_one};
use core::prelude::*;
use core::result;
use core::run;
use core::send_map::linear::LinearMap;
use core::task;
use core::to_bytes;

/**
*
* This is a loose clone of the fbuild build system, made a touch more
* generic (not wired to special cases on files) and much less metaprogram-y
* due to rust's comparative weakness there, relative to python.
*
* It's based around _imperative bulids_ that happen to have some function
* calls cached. That is, it's _just_ a mechanism for describing cached
* functions. This makes it much simpler and smaller than a "build system"
* that produces an IR and evaluates it. The evaluation order is normal
* function calls. Some of them just return really quickly.
*
* A cached function consumes and produces a set of _works_. A work has a
* name, a kind (that determines how the value is to be checked for
* freshness) and a value. Works must also be (de)serializable. Some
* examples of works:
*
*    kind   name    value
*   ------------------------
*    cfg    os      linux
*    file   foo.c   <sha1>
*    url    foo.com <etag>
*
* Works are conceptually single units, but we store them most of the time
* in maps of the form (type,name) => value. These are WorkMaps.
*
* A cached function divides the works it's interested up into inputs and
* outputs, and subdivides those into declared (input and output) works and
* discovered (input and output) works.
*
* A _declared_ input or output is one that is given to the workcache before
* any work actually happens, in the "prep" phase. Even when a function's
* work-doing part (the "exec" phase) never gets called, it has declared
* inputs and outputs, which can be checked for freshness (and potentially
* used to determine that the function can be skipped).
*
* The workcache checks _all_ works for freshness, but uses the set of
* discovered outputs from the _previous_ exec (which it will re-discover
* and re-record each time the exec phase runs).
*
* Therefore the discovered works cached in the db might be a
* mis-approximation of the current discoverable works, but this is ok for
* the following reason: we assume that if an artifact A changed from
* depending on B,C,D to depending on B,C,D,E, then A itself changed (as
* part of the change-in-dependencies), so we will be ok.
*
* Each function has a single discriminated output work called its _result_.
* This is only different from other works in that it is returned, by value,
* from a call to the cacheable function; the other output works are used in
* passing to invalidate dependencies elsewhere in the cache, but do not
* otherwise escape from a function invocation. Most functions only have one
* output work anyways.
*
* A database (the central store of a workcache) stores a mappings:
*
* (fn_name,{declared_input}) => ({declared_output},{discovered_input},
*                                {discovered_output},result)
*
*/

#[deriving_eq]
struct WorkKey {
    kind: ~str,
    name: ~str
}

impl WorkKey: to_bytes::IterBytes {
    #[inline(always)]
    pure fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
        let mut flag = true;
        self.kind.iter_bytes(lsb0, |bytes| {flag = f(bytes); flag});
        if !flag { return; }
        self.name.iter_bytes(lsb0, f);
    }
}

impl WorkKey {
    static fn new(kind: &str, name: &str) -> WorkKey {
    WorkKey { kind: kind.to_owned(), name: name.to_owned() }
    }
}

type WorkMap = LinearMap<WorkKey, ~str>;

struct Database {
    // FIXME #4432: Fill in.
    a: ()
}

impl Database {
    pure fn prepare(_fn_name: &str,
                    _declared_inputs: &const WorkMap,
                    _declared_outputs: &const WorkMap) ->
        Option<(WorkMap, WorkMap, ~str)> {
        // FIXME #4432: load
        None
    }
    pure fn cache(_fn_name: &str,
                  _declared_inputs: &WorkMap,
                  _declared_outputs: &WorkMap,
                  _discovered_inputs: &WorkMap,
                  _discovered_outputs: &WorkMap,
                  _result: &str) {
        // FIXME #4432: store
    }
}

struct Logger {
    // FIXME #4432: Fill in
    a: ()
}

impl Logger {
    pure fn info(i: &str) {
        unsafe {
            io::println(~"workcache: " + i.to_owned());
        }
    }
}

struct Context {
    db: @Database,
    logger: @Logger,
    cfg: @json::Object,
    freshness: LinearMap<~str,@pure fn(&str,&str)->bool>
}

struct Prep {
    ctxt: @Context,
    fn_name: ~str,
    declared_inputs: WorkMap,
    declared_outputs: WorkMap
}

struct Exec {
    discovered_inputs: WorkMap,
    discovered_outputs: WorkMap
}

struct Work<T:Owned> {
    prep: @mut Prep,
    res: Option<Either<T,PortOne<(Exec,T)>>>
}

fn digest<T:Encodable<json::Encoder>
            Decodable<json::Decoder>>(t: &T) -> ~str {
    let sha = sha1::sha1();
    let s = do io::with_str_writer |wr| {
        // FIXME #4432: sha1 should be a writer itself, shouldn't
        // go via strings.
        t.encode(&json::Encoder(wr));
    };
    sha.input_str(s);
    sha.result_str()
}

fn digest_file(path: &Path) -> ~str {
    let sha = sha1::sha1();
    let s = io::read_whole_file_str(path);
    sha.input_str(*s.get_ref());
    sha.result_str()
}

impl Context {

    static fn new(db: @Database, lg: @Logger,
                  cfg: @json::Object) -> Context {
        Context {db: db, logger: lg, cfg: cfg, freshness: LinearMap()}
    }

    fn prep<T:Owned
              Encodable<json::Encoder>
              Decodable<json::Decoder>>(
                  @self,
                  fn_name:&str,
                  blk: fn((@mut Prep))->Work<T>) -> Work<T> {
        let p = @mut Prep {ctxt: self,
                           fn_name: fn_name.to_owned(),
                           declared_inputs: LinearMap(),
                           declared_outputs: LinearMap()};
        blk(p)
    }
}

impl Prep {
    fn declare_input(&mut self, kind:&str, name:&str, val:&str) {
        self.declared_inputs.insert(WorkKey::new(kind, name),
                                    val.to_owned());
    }

    fn declare_output(&mut self, kind:&str, name:&str, val:&str) {
        self.declared_outputs.insert(WorkKey::new(kind, name),
                                     val.to_owned());
    }

    pure fn is_fresh(cat: &str, kind: &str,
                     name: &str, val: &str) -> bool {
        let k = kind.to_owned();
        let f = (self.ctxt.freshness.get(&k))(name, val);
        if f {
            self.ctxt.logger.info(fmt!("%s %s:%s is fresh",
                                       cat, kind, name));
        } else {
            self.ctxt.logger.info(fmt!("%s %s:%s is not fresh",
                                       cat, kind, name))
        }
        return f;
    }

    pure fn all_fresh(cat: &str, map: WorkMap) -> bool {
        for map.each |k,v| {
            if ! self.is_fresh(cat, k.kind, k.name, *v) {
                return false;
            }
        }
        return true;
    }

    fn exec<T:Owned
              Encodable<json::Encoder>
              Decodable<json::Decoder>>(
                  @mut self, blk: ~fn(&Exec) -> T) -> Work<T> {

        let cached = self.ctxt.db.prepare(self.fn_name,
                                          &self.declared_inputs,
                                          &self.declared_outputs);

        match move cached {
            None => (),
            Some((move disc_in,
                  move disc_out,
                  move res)) => {

                if self.all_fresh("declared input",
                                  self.declared_inputs) &&
                    self.all_fresh("declared output",
                                   self.declared_outputs) &&
                    self.all_fresh("discovered input", disc_in) &&
                    self.all_fresh("discovered output", disc_out) {

                    let v : T = do io::with_str_reader(res) |rdr| {
                        let j = result::unwrap(json::from_reader(rdr));
                        Decodable::decode(&json::Decoder(move j))
                    };
                    return Work::new(self, move Left(move v));
                }
            }
        }

        let (chan, port) = oneshot::init();

        let chan = ~mut Some(move chan);
        do task::spawn |move blk, move chan| {
            let exe = Exec { discovered_inputs: LinearMap(),
                             discovered_outputs: LinearMap() };
            let chan = option::swap_unwrap(&mut *chan);
            let v = blk(&exe);
            send_one(move chan, (move exe, move v));
        }

        Work::new(self, move Right(move port))
    }
}

impl<T:Owned
       Encodable<json::Encoder>
       Decodable<json::Decoder>>
    Work<T> {
    static fn new(p: @mut Prep, e: Either<T,PortOne<(Exec,T)>>) -> Work<T> {
        move Work { prep: p, res: Some(move e) }
    }
}

// FIXME (#3724): movable self. This should be in impl Work.
fn unwrap<T:Owned
            Encodable<json::Encoder>
            Decodable<json::Decoder>>(w: Work<T>) -> T {

    let mut ww = move w;
    let mut s = None;

    ww.res <-> s;

    match move s {
        None => fail,
        Some(Left(move v)) => move v,
        Some(Right(move port)) => {

            let (exe, v) = match recv(move port) {
                oneshot::send(move data) => move data
            };

            let s = do io::with_str_writer |wr| {
                v.encode(&json::Encoder(wr));
            };

            ww.prep.ctxt.db.cache(ww.prep.fn_name,
                                  &ww.prep.declared_inputs,
                                  &ww.prep.declared_outputs,
                                  &exe.discovered_inputs,
                                  &exe.discovered_outputs,
                                  s);
            move v
        }
    }
}

#[test]
fn test() {
    use io::WriterUtil;

    let db = @Database { a: () };
    let lg = @Logger { a: () };
    let cfg = @LinearMap();
    let cx = @Context::new(db, lg, cfg);
    let w:Work<~str> = do cx.prep("test1") |prep| {
        let pth = Path("foo.c");
        {
            let file = io::file_writer(&pth, [io::Create]).get();
            file.write_str("int main() { return 0; }");
        }

        prep.declare_input("file", pth.to_str(), digest_file(&pth));
        do prep.exec |_exe| {
            let out = Path("foo.o");
            run::run_program("gcc", [~"foo.c", ~"-o", out.to_str()]);
            move out.to_str()
        }
    };
    let s = unwrap(move w);
    io::println(s);
}
<commit_msg>std: various hacking on workcache.<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use json;
use sha1;
use serialize::{Encoder, Encodable, Decoder, Decodable};
use sort;

use core::cmp;
use core::dvec;
use core::either::{Either, Left, Right};
use core::io;
use core::option;
use core::pipes::{recv, oneshot, PortOne, send_one};
use core::prelude::*;
use core::result;
use core::run;
use core::send_map::linear::LinearMap;
use core::task;
use core::to_bytes;
use core::mutable::Mut;

/**
*
* This is a loose clone of the fbuild build system, made a touch more
* generic (not wired to special cases on files) and much less metaprogram-y
* due to rust's comparative weakness there, relative to python.
*
* It's based around _imperative bulids_ that happen to have some function
* calls cached. That is, it's _just_ a mechanism for describing cached
* functions. This makes it much simpler and smaller than a "build system"
* that produces an IR and evaluates it. The evaluation order is normal
* function calls. Some of them just return really quickly.
*
* A cached function consumes and produces a set of _works_. A work has a
* name, a kind (that determines how the value is to be checked for
* freshness) and a value. Works must also be (de)serializable. Some
* examples of works:
*
*    kind   name    value
*   ------------------------
*    cfg    os      linux
*    file   foo.c   <sha1>
*    url    foo.com <etag>
*
* Works are conceptually single units, but we store them most of the time
* in maps of the form (type,name) => value. These are WorkMaps.
*
* A cached function divides the works it's interested up into inputs and
* outputs, and subdivides those into declared (input) works and
* discovered (input and output) works.
*
* A _declared_ input or is one that is given to the workcache before
* any work actually happens, in the "prep" phase. Even when a function's
* work-doing part (the "exec" phase) never gets called, it has declared
* inputs, which can be checked for freshness (and potentially
* used to determine that the function can be skipped).
*
* The workcache checks _all_ works for freshness, but uses the set of
* discovered outputs from the _previous_ exec (which it will re-discover
* and re-record each time the exec phase runs).
*
* Therefore the discovered works cached in the db might be a
* mis-approximation of the current discoverable works, but this is ok for
* the following reason: we assume that if an artifact A changed from
* depending on B,C,D to depending on B,C,D,E, then A itself changed (as
* part of the change-in-dependencies), so we will be ok.
*
* Each function has a single discriminated output work called its _result_.
* This is only different from other works in that it is returned, by value,
* from a call to the cacheable function; the other output works are used in
* passing to invalidate dependencies elsewhere in the cache, but do not
* otherwise escape from a function invocation. Most functions only have one
* output work anyways.
*
* A database (the central store of a workcache) stores a mappings:
*
* (fn_name,{declared_input}) => ({discovered_input},
*                                {discovered_output},result)
*
* (Note: fbuild, which workcache is based on, has the concept of a declared
* output as separate from a discovered output. This distinction exists only
* as an artifact of how fbuild works: via annotations on function types
* and metaprogramming, with explicit dependency declaration as a fallback.
* Workcache is more explicit about dependencies, and as such treats all
* outputs the same, as discovered-during-the-last-run.)
*
*/

#[deriving_eq]
#[auto_encode]
#[auto_decode]
struct WorkKey {
    kind: ~str,
    name: ~str
}

impl WorkKey: to_bytes::IterBytes {
    #[inline(always)]
    pure fn iter_bytes(&self, lsb0: bool, f: to_bytes::Cb) {
        let mut flag = true;
        self.kind.iter_bytes(lsb0, |bytes| {flag = f(bytes); flag});
        if !flag { return; }
        self.name.iter_bytes(lsb0, f);
    }
}

impl WorkKey: cmp::Ord {
    pure fn lt(&self, other: &WorkKey) -> bool {
        self.kind < other.kind ||
            (self.kind == other.kind &&
             self.name < other.name)
    }
    pure fn le(&self, other: &WorkKey) -> bool {
        self.lt(other) || self.eq(other)
    }
    pure fn ge(&self, other: &WorkKey) -> bool {
        self.gt(other) || self.eq(other)
    }
    pure fn gt(&self, other: &WorkKey) -> bool {
        ! self.le(other)
    }
}

impl WorkKey {
    static fn new(kind: &str, name: &str) -> WorkKey {
    WorkKey { kind: kind.to_owned(), name: name.to_owned() }
    }
}

type WorkMap = LinearMap<WorkKey, ~str>;

pub impl<S: Encoder> WorkMap: Encodable<S> {
    fn encode(&self, s: &S) {
        let d = dvec::DVec();
        for self.each |k, v| {
            d.push((copy *k, copy *v))
        }
        let mut v = d.get();
        sort::tim_sort(v);
        v.encode(s)
    }
}

pub impl<D: Decoder> WorkMap: Decodable<D> {
    static fn decode(&self, d: &D) -> WorkMap {
        let v : ~[(WorkKey,~str)] = Decodable::decode(d);
        let mut w = LinearMap();
        for v.each |&(k,v)| {
            w.insert(copy k, copy v);
        }
        w
    }
}

struct Database {
    db_filename: Path,
    db_cache: LinearMap<~str, ~str>,
    mut db_dirty: bool
}

impl Database {

    fn prepare(&mut self,
               fn_name: &str,
               declared_inputs: &WorkMap) ->
        Option<(WorkMap, WorkMap, ~str)> {
        let k = json_encode(&(fn_name, declared_inputs));
        match self.db_cache.find(&k) {
            None => None,
            Some(v) => Some(json_decode(v))
        }
    }

    fn cache(&mut self,
             fn_name: &str,
             declared_inputs: &WorkMap,
             discovered_inputs: &WorkMap,
             discovered_outputs: &WorkMap,
             result: &str) {
        let k = json_encode(&(fn_name, declared_inputs));
        let v = json_encode(&(discovered_inputs,
                              discovered_outputs,
                              result));
        self.db_cache.insert(k,v);
        self.db_dirty = true
    }
}

struct Logger {
    // FIXME #4432: Fill in
    a: ()
}

impl Logger {
    fn info(i: &str) {
        io::println(~"workcache: " + i.to_owned());
    }
}

struct Context {
    db: @Mut<Database>,
    logger: @Mut<Logger>,
    cfg: @json::Object,
    freshness: LinearMap<~str,@fn(&str,&str)->bool>
}

struct Prep {
    ctxt: @Context,
    fn_name: ~str,
    declared_inputs: WorkMap,
}

struct Exec {
    discovered_inputs: WorkMap,
    discovered_outputs: WorkMap
}

struct Work<T:Owned> {
    prep: @Mut<Prep>,
    res: Option<Either<T,PortOne<(Exec,T)>>>
}

fn json_encode<T:Encodable<json::Encoder>>(t: &T) -> ~str {
    do io::with_str_writer |wr| {
        t.encode(&json::Encoder(wr));
    }
}

fn json_decode<T:Decodable<json::Decoder>>(s: &str) -> T {
    do io::with_str_reader(s) |rdr| {
        let j = result::unwrap(json::from_reader(rdr));
        Decodable::decode(&json::Decoder(move j))
    }
}

fn digest<T:Encodable<json::Encoder>>(t: &T) -> ~str {
    let sha = sha1::sha1();
    sha.input_str(json_encode(t));
    sha.result_str()
}

fn digest_file(path: &Path) -> ~str {
    let sha = sha1::sha1();
    let s = io::read_whole_file_str(path);
    sha.input_str(*s.get_ref());
    sha.result_str()
}

impl Context {

    static fn new(db: @Mut<Database>,
                  lg: @Mut<Logger>,
                  cfg: @json::Object) -> Context {
        Context {db: db, logger: lg, cfg: cfg, freshness: LinearMap()}
    }

    fn prep<T:Owned
              Encodable<json::Encoder>
              Decodable<json::Decoder>>(
                  @self,
                  fn_name:&str,
                  blk: fn(@Mut<Prep>)->Work<T>) -> Work<T> {
        let p = @Mut(Prep {ctxt: self,
                           fn_name: fn_name.to_owned(),
                           declared_inputs: LinearMap()});
        blk(p)
    }
}

trait TPrep {
    fn declare_input(&self, kind:&str, name:&str, val:&str);
    fn is_fresh(&self, cat:&str, kind:&str, name:&str, val:&str) -> bool;
    fn all_fresh(&self, cat:&str, map:&WorkMap) -> bool;
    fn exec<T:Owned
        Encodable<json::Encoder>
        Decodable<json::Decoder>>(&self, blk: ~fn(&Exec) -> T) -> Work<T>;
}

impl @Mut<Prep> : TPrep {
    fn declare_input(&self, kind:&str, name:&str, val:&str) {
        do self.borrow_mut |p| {
            p.declared_inputs.insert(WorkKey::new(kind, name),
                                     val.to_owned());
        }
    }

    fn is_fresh(&self, cat: &str, kind: &str,
                name: &str, val: &str) -> bool {
        do self.borrow_imm |p| {
            let k = kind.to_owned();
            let f = (p.ctxt.freshness.get(&k))(name, val);
            do p.ctxt.logger.borrow_imm |lg| {
                if f {
                    lg.info(fmt!("%s %s:%s is fresh",
                                 cat, kind, name));
                } else {
                    lg.info(fmt!("%s %s:%s is not fresh",
                                 cat, kind, name))
                }
            }
            f
        }
    }

    fn all_fresh(&self, cat: &str, map: &WorkMap) -> bool {
        for map.each |k,v| {
            if ! self.is_fresh(cat, k.kind, k.name, *v) {
                return false;
            }
        }
        return true;
    }

    fn exec<T:Owned
        Encodable<json::Encoder>
        Decodable<json::Decoder>>(&self,
                                  blk: ~fn(&Exec) -> T) -> Work<T> {

        let mut bo = Some(move blk);

        do self.borrow_imm |p| {
            let cached = do p.ctxt.db.borrow_mut |db| {
                db.prepare(p.fn_name, &p.declared_inputs)
            };

            match move cached {
                Some((ref disc_in, ref disc_out, ref res))
                if self.all_fresh("declared input",
                                  &p.declared_inputs) &&
                self.all_fresh("discovered input", disc_in) &&
                self.all_fresh("discovered output", disc_out) => {
                    Work::new(*self, move Left(json_decode(*res)))
                }

                _ => {
                    let (chan, port) = oneshot::init();
                    let mut blk = None;
                    blk <-> bo;
                    let blk = blk.unwrap();
                    let chan = ~mut Some(move chan);
                    do task::spawn |move blk, move chan| {
                        let exe = Exec { discovered_inputs: LinearMap(),
                                         discovered_outputs: LinearMap() };
                        let chan = option::swap_unwrap(&mut *chan);
                        let v = blk(&exe);
                        send_one(move chan, (move exe, move v));
                    }

                    Work::new(*self, move Right(move port))
                }
            }
        }
    }
}

impl<T:Owned
       Encodable<json::Encoder>
       Decodable<json::Decoder>>
    Work<T> {
    static fn new(p: @Mut<Prep>, e: Either<T,PortOne<(Exec,T)>>) -> Work<T> {
        move Work { prep: p, res: Some(move e) }
    }
}

// FIXME (#3724): movable self. This should be in impl Work.
fn unwrap<T:Owned
            Encodable<json::Encoder>
            Decodable<json::Decoder>>(w: Work<T>) -> T {

    let mut ww = move w;
    let mut s = None;

    ww.res <-> s;

    match move s {
        None => fail,
        Some(Left(move v)) => move v,
        Some(Right(move port)) => {

            let (exe, v) = match recv(move port) {
                oneshot::send(move data) => move data
            };

            let s = json_encode(&v);

            do ww.prep.borrow_imm |p| {
                do p.ctxt.db.borrow_mut |db| {
                    db.cache(p.fn_name,
                             &p.declared_inputs,
                             &exe.discovered_inputs,
                             &exe.discovered_outputs,
                             s);
                }
            }
            move v
        }
    }
}

//#[test]
fn test() {
    use io::WriterUtil;

    let db = @Mut(Database { db_filename: Path("db.json"),
                             db_cache: LinearMap(),
                             db_dirty: false });
    let lg = @Mut(Logger { a: () });
    let cfg = @LinearMap();
    let cx = @Context::new(db, lg, cfg);
    let w:Work<~str> = do cx.prep("test1") |prep| {
        let pth = Path("foo.c");
        {
            let file = io::file_writer(&pth, [io::Create]).get();
            file.write_str("int main() { return 0; }");
        }

        prep.declare_input("file", pth.to_str(), digest_file(&pth));
        do prep.exec |_exe| {
            let out = Path("foo.o");
            run::run_program("gcc", [~"foo.c", ~"-o", out.to_str()]);
            move out.to_str()
        }
    };
    let s = unwrap(move w);
    io::println(s);
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;
use module::Module;

type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend<'a> {
    basepath: String,
    module: &'a Module,
}

impl<'a> StorageBackend<'a> {

    fn new(basepath: String, module: &'a Module) -> StorageBackend<'a> {
        StorageBackend {
            basepath: basepath,
            module: module,
        }
    }

    fn getFileList(&self) -> Option<Vec<FileID>> {
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    fn createEmpty() -> FileID {
    }

    fn createFile() -> File {
    }

    fn writeFile(f: File) -> BackendOperationResult {
    }

    fn createFileWithContent(content: String) -> BackendOperationResult {
    }

    fn readFile(id: FileID) -> String {
    }

    // TODO: Meta files are not covered yet

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub dataDump: Option<String>    // Data dump, if any
}

impl StorageBackendError {
    fn new(action: &'static str,
           desc  : &'static str,
           explan: &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            explanation:    String::from(explan),
            dataDump:       data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<commit_msg>We dont need to pass the module here<commit_after>use std::error::Error;
use std::fmt::Display;
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use glob::glob;
use glob::Paths;

use storage::file::File;
use storage::file_id::*;

type BackendOperationResult = Result<(), StorageBackendError>;

pub struct StorageBackend {
    basepath: String,
}

impl StorageBackend {

    fn new(basepath: String) -> StorageBackend {
        StorageBackend {
            basepath: basepath,
        }
    }

    fn getFileList(&self) -> Option<Vec<FileID>> {
        let list = glob(&self.basepath[..]);

        if let Ok(globlist) = list {
            let mut v = vec![];
            for entry in globlist {
                if let Ok(path) = entry {
                    v.push(from_pathbuf(&path));
                } else {
                    // Entry is not a path
                }
            }

            Some(v)
        } else {
            None
        }
    }

    fn createEmpty() -> FileID {
    }

    fn createFile() -> File {
    }

    fn writeFile(f: File) -> BackendOperationResult {
    }

    fn createFileWithContent(content: String) -> BackendOperationResult {
    }

    fn readFile(id: FileID) -> String {
    }

    // TODO: Meta files are not covered yet

}

#[derive(Debug)]
pub struct StorageBackendError {
    pub action: String,             // The file system action in words
    pub desc: String,               // A short description
    pub explanation: String,        // A long, user friendly description
    pub dataDump: Option<String>    // Data dump, if any
}

impl StorageBackendError {
    fn new(action: &'static str,
           desc  : &'static str,
           explan: &'static str,
           data  : Option<String>) -> StorageBackendError
    {
        StorageBackendError {
            action:         String::from(action),
            desc:           String::from(desc),
            explanation:    String::from(explan),
            dataDump:       data,
        }
    }
}

impl Error for StorageBackendError {

    fn description(&self) -> &str {
        &self.desc[..]
    }

    fn cause(&self) -> Option<&Error> {
        None
    }

}

impl Display for StorageBackendError {
    fn fmt(&self, f: &mut Formatter) -> FMTResult {
        write!(f, "StorageBackendError[{}]: {}\n\n{}",
               self.action, self.desc, self.explanation)
    }
}

<|endoftext|>


<commit_before>use editor::keyboard::{Key};

// A motion can vary a column in the following ways:
// * not varying it
// * changing it relatively
// * going to a specific column
// * going to a conceptual column (eol, first-non-blank)
#[derive(Debug)]
pub enum Column {
    Current,
    Left(usize), Right(usize),
    Specific(usize),
    // Begin is the first-non-blank. 0 should use specific
    Begin, End
}

enum SkipThing {
	Word, Sentence, Whitespace,
}

#[derive(Debug)]
pub enum Line {
    Current,
    Up(usize), Down(usize),
    Specific(usize),
    Last
}

#[derive(Debug)]
pub enum Span {
    Inclusive,
    Exclusive,
    Linewise,
}

#[derive(Debug)]
pub enum Motion {
    Goto(Column,Line),
    Scroll(Line),
}

pub enum Operator {
	Delete,
	Insert,
}

pub enum Action {
	Operator(Operator),
	Motion(Motion),
}

#[derive(Debug)]
pub struct Command {
	pub count: usize,
    pub span: Span,
    pub motion: Motion,
}

impl Command {
    pub fn goto(s: Span, c: Column, l: Line) -> Command {
	    Command{count: 1, span: s, motion: Motion::Goto(c, l) }
	}

	pub fn scroll(l: Line) -> Command {
	    Command{count: 1, span: Span::Linewise, motion: Motion::Scroll(l) }
	}
}

#[derive(Debug)]
enum BuilderResult {
    Invalid,
    Pending,
    Command(Command),
}

struct Builder {
	// TODO; what goes here
	count: Option<usize>,
}

impl Builder {
    pub fn new() -> Builder {
        Builder {
        	count: None
        }
    }
    pub fn handle_key(&mut self, key: Key) -> BuilderResult {
        if let Key::Char(c) = key {
        	if c.is_digit(10) {
        		let c = c.to_digit(10).unwrap() as usize;
        		match self.count {
        			None => {
        				self.count = Some(c);
        			}
        			Some(n) => {
        				self.count = Some(n * 10 + c);
        			}
        		};
        		return BuilderResult::Pending;
        	}
        }

      	BuilderResult::Invalid
    }
}

#[test]
fn builder_enter_count() {
    let mut builder = Builder::new();
    let cmd = builder.handle_key(Key::Char('1'));

    match cmd {
        BuilderResult::Pending => assert!(true),
        _ => assert!(false, "should be pending"),
    }

    let cmd = builder.handle_key(Key::Char('2'));
    match cmd {
        BuilderResult::Pending => assert!(true),
        _ => assert!(false, "should be pending"),
    }
    assert_eq!(builder.count, Some(12));
}

<commit_msg>more builder junk<commit_after>use editor::keyboard::{Key};

// A motion can vary a column in the following ways:
// * not varying it
// * changing it relatively
// * going to a specific column
// * going to a conceptual column (eol, first-non-blank)
#[derive(Debug)]
pub enum Column {
    Current,
    Left(usize), Right(usize),
    Specific(usize),
    // Begin is the first-non-blank. 0 should use specific
    Begin, End
}

enum SkipThing {
	Word, Sentence, Whitespace,
}

#[derive(Debug)]
pub enum Line {
    Current,
    Up(usize), Down(usize),
    Specific(usize),
    Last
}

#[derive(Debug)]
pub enum Span {
    Inclusive,
    Exclusive,
    Linewise,
}

#[derive(Debug)]
pub enum Motion {
    Goto(Column,Line),
    Scroll(Line),
}

pub enum Operator {
	Delete,
	Insert,
}

pub enum Action {
	Operator(Operator),
	Motion(Motion),
}

#[derive(Debug)]
pub struct Command {
	pub count: usize,
    pub span: Span,
    pub motion: Motion,
}

impl Command {
    pub fn goto(s: Span, c: Column, l: Line) -> Command {
	    Command{count: 1, span: s, motion: Motion::Goto(c, l) }
	}

	pub fn scroll(l: Line) -> Command {
	    Command{count: 1, span: Span::Linewise, motion: Motion::Scroll(l) }
	}
}

enum TextObject {
	Char, Word, Sentence, Line, Paragraph,
}
enum Partial {
	Object(TextObject),
	Action(Action),
}

#[derive(Debug)]
enum BuilderResult {
    Invalid,
    Pending,
    Command(Command),
}

struct Builder {
	// TODO; what goes here
	count: Option<usize>,
}

impl Builder {
    pub fn new() -> Builder {
        Builder {
        	count: None
        }
    }
    pub fn handle_key(&mut self, key: Key) -> BuilderResult {
        if let Key::Char(c) = key {
        	if c.is_digit(10) {
        		let c = c.to_digit(10).unwrap() as usize;
        		match self.count {
        			None => {
        				self.count = Some(c);
        			}
        			Some(n) => {
        				self.count = Some(n * 10 + c);
        			}
        		};
        		return BuilderResult::Pending;
        	}
        }

        match self.lookup_key(key) {
            Some(partial) => {
            	let cmd = self.build_cmd(partial);
            	return BuilderResult::Command(cmd);
            }
            None => {
            	return BuilderResult::Invalid;
            }
        }
      	BuilderResult::Invalid
    }

    fn build_cmd(&mut self, partial: Partial) -> Command {
        Command {
        	count: self.count.unwrap_or(1),
        	span: Span::Linewise,
        	motion: match partial {
        		Partial::Action(action) => match action {
        		    Action::Motion(motion) => motion,
        		    _ => panic!("{:?}", "oh no")
        		},
        		_ => {
        			panic!("{:?}", "oh no");
        		}
        	}
        }
    }

    fn lookup_key(&self, key: Key) -> Option<Partial> {
    	match key {
            Key::Ctrl('h') | Key::Char('h') | Key::Left  => {
                Some(Partial::Action(Action::Motion(Motion::Goto(Column::Left(1), Line::Current))))
            }
            // Key::Ctrl('j') | Key::Char('j') | Key::Down  => {
            //     Some(Command::goto(Span::Linewise, Column::Current, Line::Down(1)))
            // }
            // Key::Ctrl('p') | Key::Char('k') | Key::Up  => {
            //     Some(Command::goto(Span::Linewise, Column::Current, Line::Up(1)))
            // }
            // Key::Char(' ') | Key::Char('l') | Key::Right  => {
            //     Some(Command::goto(Span::Exclusive, Column::Right(1), Line::Current))
            // }
            // Key::Char('0')  => {
            //     Some(Command::goto(Span::Exclusive, Column::Specific(0), Line::Current))
            // }
            // Key::Char('^')  => {
            //     Some(Command::goto(Span::Exclusive, Column::Begin, Line::Current))
            // }
            // Key::Char('$')  => {
            //     Some(Command::goto(Span::Exclusive, Column::End, Line::Current))
            // }
            // Key::Char('G')  => {
            //     Some(Command::goto(Span::Linewise, Column::Current, Line::Last))
            // }
            // Key::Char('+') | Key::Char('m') => {
            //     Some(Command::goto(Span::Exclusive, Column::Begin, Line::Down(1)))
            // }
            // Key::Char('-') => {
            //     Some(Command::goto(Span::Exclusive, Column::Begin, Line::Up(1)))
            // }
            // Key::Ctrl('b') => {
            //     // what an awful way to do this.
            //     let lines = self.active().unwrap().window().1;
            //     let count = 1; // TODO: actually use [count]
            //     let relative = (count * (lines - 2));
            //     Some(Command::scroll(Line::Up(relative)))
            // }
            // Key::Ctrl('f') => {
            //     // what an awful way to do this.
            //     let lines = self.active().unwrap().window().1;
            //     let count = 1; // TODO: actually use [count]
            //     let relative = (count * (lines - 2));
            //     Some(Command::scroll(Line::Down(relative)))
            // }
            // Key::Ctrl('d') => {
            //     // what an awful way to do this.
            //     let lines = self.active().unwrap().window().1;
            //     let count = 1; // TODO: actually use [count]
            //     let relative = (count * (lines / 2));
            //     Some(Command::scroll(Line::Down(relative)))
            // }
            // Key::Ctrl('u') => {
            //     // what an awful way to do this.
            //     let lines = self.active().unwrap().window().1;
            //     let count = 1; // TODO: actually use [count]
            //     let relative = (count * (lines / 2));
            //     Some(Command::scroll(Line::Up(relative)))
            // }
            // Key::Ctrl('e') => {
            //     Some(Command::scroll(Line::Down(1)))
            // }
            // Key::Ctrl('y') => {
            //     Some(Command::scroll(Line::Up(1)))
            // }
            _ => None
        }
    }
}

#[test]
fn builder_enter_count() {
    let mut builder = Builder::new();
    let cmd = builder.handle_key(Key::Char('1'));

    match cmd {
        BuilderResult::Pending => assert!(true),
        _ => assert!(false, "should be pending"),
    }

    let cmd = builder.handle_key(Key::Char('2'));
    match cmd {
        BuilderResult::Pending => assert!(true),
        _ => assert!(false, "should be pending"),
    }
    assert_eq!(builder.count, Some(12));
}

<|endoftext|>


<commit_before>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};
use std::ffi::CString;
use std::ops::Deref;
use std::ptr;

pub type Display = *const c_void;
pub type Window = c_int;

pub type KeySym = *const c_void;
pub type KeyCode = c_uint;

type Bool = c_int;

#[link(name = "X11")]
extern "C" {
    fn XOpenDisplay(string: *const c_char) -> Display;
    fn XRootWindow(display: Display, index: c_int) -> Window;
    fn XFree(data: *const c_void) -> c_int;
    fn XFlush(display: Display) -> c_int;

    fn XStringToKeysym(string: *const c_char) -> KeySym;
    fn XKeysymToKeycode(display: Display, keysym: KeySym, index: c_int) -> KeyCode;
    fn XChangeKeyboardMapping(
        display: Display,
        first_keycode: c_int,
        keycode_count: c_int,
        keysyms: *const KeySym,
        keysyms_per_keycode_return: c_int,
    ) -> KeySym;
    fn XGetKeyboardMapping(
        display: Display,
        first_keycode: KeyCode,
        keycode_count: c_int,
        keysyms_per_keycode_return: *mut c_int,
    ) -> *mut KeySym;
    fn XDisplayKeycodes(
        display: Display,
        min_keycodes_return: *mut c_int,
        max_keycodes_return: *mut c_int,
    ) -> c_int;

    fn XWarpPointer(
        display: Display,
        src_w: Window,
        dest_w: Window,
        src_x: c_int,
        src_y: c_int,
        src_width: c_int,
        src_height: c_int,
        dest_x: c_int,
        dest_y: c_int,
    );
}

#[link(name = "Xtst")]
extern "C" {
    fn XTestFakeKeyEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
    fn XTestFakeButtonEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
}

/// The main struct for handling the event emitting
pub struct Enigo {
    display: Display,
    window: Window,
}

impl Enigo {
    /// Constructs a new `Enigo` instance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use enigo::*;
    /// let mut enigo = Enigo::new();
    /// ```
    pub fn new() -> Self {
        let display;
        unsafe { display = XOpenDisplay(ptr::null()) };
        if display.is_null() {
            panic!("can't open display");
        }

        let window = unsafe { XRootWindow(display, 0) };

        Enigo {
            display: display,
            window: window,
        }
    }

    // TODO(dustin): implement drop
}

impl Default for Enigo {
    fn default() -> Self {
        Self::new()
    }
}

impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, self.window, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_move_relative(&mut self, x: i32, y: i32) {

        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, 0, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_down(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                1,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_up(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                0,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_click(&mut self, button: MouseButton) {
        use std::{thread, time};

        self.mouse_down(button);
        thread::sleep(time::Duration::from_millis(100));
        self.mouse_up(button);
    }

    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }

    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }
}

impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        for c in sequence.chars() {
            let rust_unicode: String = format!("U{:x}", c as u32);
            let keycode = self.unicode_string_to_keycode(&rust_unicode);

            self.keycode_click(*keycode);
        }
    }

    fn key_click(&mut self, key: Key) {
        self.keycode_click(self.key_to_keycode(key));
    }

    fn key_down(&mut self, key: Key) {
        self.keycode_down(self.key_to_keycode(key));
    }

    fn key_up(&mut self, key: Key) {
        self.keycode_up(self.key_to_keycode(key));
    }
}

struct KeyGuard<'a>(&'a Enigo, u32);

impl<'a> Deref for KeyGuard<'a> {
	type Target = u32;

	fn deref(&self) -> &Self::Target {
		return &self.1;
	}
}
impl<'a> Drop for KeyGuard<'a> {
	fn drop(&mut self) {
		self.0.reset_keycode(self.1);
	}
}

impl Enigo {
    fn reset_keycode(&self, keycode: u32) {
        unsafe {
            let keysym_list = [0 as KeySym, 0 as KeySym].as_ptr();
            XChangeKeyboardMapping(self.display, keycode as i32, 2, keysym_list, 1);
        }
    }

    fn unicode_string_to_keycode(&self, unicode_string: &str) -> KeyGuard {
        let mut keysyms_per_keycode = 0;
        // scratch space for temporary keycode bindings
        let mut scratch_keycode = 0;
        let mut keycode_low = 0;
        let mut keycode_high = 0;

        let keysyms = unsafe {
            // get the range of keycodes usually from 8 - 255
            XDisplayKeycodes(self.display, &mut keycode_low, &mut keycode_high);
            // get all the mapped keysysms available
            let keycode_count = keycode_high - keycode_low;
            XGetKeyboardMapping(
                self.display,
                keycode_low as u32,
                keycode_count,
                &mut keysyms_per_keycode,
            )
        };

        // find unused keycode for unmapped keysyms so we can
        // hook up our own keycode and map every keysym on it
        // so we just need to 'click' our once unmapped keycode
        for cidx in keycode_low..keycode_high + 1 {
            let mut key_is_empty = true;
            for sidx in 0..keysyms_per_keycode {
                let map_idx = (cidx - keycode_low) * keysyms_per_keycode + sidx;
                let sym_at_idx = unsafe { keysyms.offset(map_idx as isize) };
                if unsafe { *sym_at_idx } != 0 as *const c_void {
                    key_is_empty = false;
                } else {
                    break;
                }
            }
            if key_is_empty {
                scratch_keycode = cidx;
                break;
            }
        }

        unsafe {
            XFree(keysyms as *mut c_void);
            XFlush(self.display);
        }

        // TODO(dustin) make this an error!
        if scratch_keycode == 0 {
            panic!("cannot find free keycode");
        }

        // find the keysym for the given unicode char
        // map that keysym to our previous unmapped keycode
        // click that keycode/'button' with our keysym on it
        let unicode_as_c_string = CString::new(unicode_string).unwrap();
        let keysym = unsafe { XStringToKeysym(unicode_as_c_string.as_ptr() as *mut c_char) };
        let keysym_list = [keysym, keysym].as_ptr();
        unsafe {
            XChangeKeyboardMapping(self.display, scratch_keycode, 2, keysym_list, 1);
            XFlush(self.display);
        }

        KeyGuard(self, scratch_keycode as u32)
    }

    fn key_to_keycode(&self, key: Key) -> u32 {
        unsafe {
            match key {
                Key::Return => XKeysymToKeycode(self.display, XK_RETURN as *const c_void, 0),
                Key::Tab => XKeysymToKeycode(self.display, XK_TAB as *const c_void, 0),
                Key::Space => XKeysymToKeycode(self.display, XK_SPACE as *const c_void, 0),
                Key::Backspace => XKeysymToKeycode(self.display, XK_BACKSPACE as *const c_void, 0),
                Key::Escape => XKeysymToKeycode(self.display, XK_ESCAPE as *const c_void, 0),
                Key::Super => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Command => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Windows => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Shift => XKeysymToKeycode(self.display, XK_SHIFT_L as *const c_void, 0),
                Key::CapsLock => XKeysymToKeycode(self.display, XK_CAPS_LOCK as *const c_void, 0),
                Key::Alt => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Option => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Control => XKeysymToKeycode(self.display, XK_CONTROL_L as *const c_void, 0),
                Key::Home => XKeysymToKeycode(self.display, XK_HOME as *const c_void, 0),
                Key::PageUp => XKeysymToKeycode(self.display, XK_PAGE_UP as *const c_void, 0),
                Key::PageDown => XKeysymToKeycode(self.display, XK_PAGE_DOWN as *const c_void, 0),
                Key::LeftArrow => XKeysymToKeycode(self.display, XK_LEFT as *const c_void, 0),
                Key::RightArrow => XKeysymToKeycode(self.display, XK_RIGHT as *const c_void, 0),
                Key::DownArrow => XKeysymToKeycode(self.display, XK_DOWN as *const c_void, 0),
                Key::UpArrow => XKeysymToKeycode(self.display, XK_UP as *const c_void, 0),
                Key::F1 => XKeysymToKeycode(self.display, XK_F1 as *const c_void, 0),
                Key::F2 => XKeysymToKeycode(self.display, XK_F2 as *const c_void, 0),
                Key::F3 => XKeysymToKeycode(self.display, XK_F3 as *const c_void, 0),
                Key::F4 => XKeysymToKeycode(self.display, XK_F4 as *const c_void, 0),
                Key::F5 => XKeysymToKeycode(self.display, XK_F5 as *const c_void, 0),
                Key::F6 => XKeysymToKeycode(self.display, XK_F6 as *const c_void, 0),
                Key::F7 => XKeysymToKeycode(self.display, XK_F7 as *const c_void, 0),
                Key::F8 => XKeysymToKeycode(self.display, XK_F8 as *const c_void, 0),
                Key::F9 => XKeysymToKeycode(self.display, XK_F9 as *const c_void, 0),
                Key::F10 => XKeysymToKeycode(self.display, XK_F10 as *const c_void, 0),
                Key::F11 => XKeysymToKeycode(self.display, XK_F11 as *const c_void, 0),
                Key::F12 => XKeysymToKeycode(self.display, XK_F12 as *const c_void, 0),

                Key::Raw(raw_keycode) => raw_keycode as u32,
                Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()),
            }
        }
    }

    fn get_layoutdependent_keycode(&self, string: String) -> u32 {
        let c_string = CString::new(string).unwrap();
        let keysym = unsafe { XStringToKeysym(c_string.as_ptr() as *mut c_char) };

        unsafe { XKeysymToKeycode(self.display, keysym, 0) }
    }

    fn keycode_click(&self, keycode: u32) {
        self.keycode_down(keycode);
        self.keycode_up(keycode);
    }

    fn keycode_down(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 1, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }

    fn keycode_up(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 0, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }
}
<commit_msg>Fix #37<commit_after>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};
use std::ffi::CString;
use std::ops::Deref;
use std::ptr;

pub type Display = *const c_void;
pub type Window = c_int;

pub type KeySym = *const c_void;
pub type KeyCode = c_uint;

type Bool = c_int;

#[link(name = "X11")]
extern "C" {
    fn XOpenDisplay(string: *const c_char) -> Display;
    fn XRootWindow(display: Display, index: c_int) -> Window;
    fn XFree(data: *const c_void) -> c_int;
    fn XFlush(display: Display) -> c_int;

    fn XStringToKeysym(string: *const c_char) -> KeySym;
    fn XKeysymToKeycode(display: Display, keysym: KeySym, index: c_int) -> KeyCode;
    fn XChangeKeyboardMapping(
        display: Display,
        first_keycode: c_int,
        keycode_count: c_int,
        keysyms: *const KeySym,
        keysyms_per_keycode_return: c_int,
    ) -> KeySym;
    fn XGetKeyboardMapping(
        display: Display,
        first_keycode: KeyCode,
        keycode_count: c_int,
        keysyms_per_keycode_return: *mut c_int,
    ) -> *mut KeySym;
    fn XDisplayKeycodes(
        display: Display,
        min_keycodes_return: *mut c_int,
        max_keycodes_return: *mut c_int,
    ) -> c_int;

    fn XWarpPointer(
        display: Display,
        src_w: Window,
        dest_w: Window,
        src_x: c_int,
        src_y: c_int,
        src_width: c_int,
        src_height: c_int,
        dest_x: c_int,
        dest_y: c_int,
    );
}

#[link(name = "Xtst")]
extern "C" {
    fn XTestFakeKeyEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
    fn XTestFakeButtonEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
}

/// The main struct for handling the event emitting
pub struct Enigo {
    display: Display,
    window: Window,
}

impl Enigo {
    /// Constructs a new `Enigo` instance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use enigo::*;
    /// let mut enigo = Enigo::new();
    /// ```
    pub fn new() -> Self {
        let display;
        unsafe { display = XOpenDisplay(ptr::null()) };
        if display.is_null() {
            panic!("can't open display");
        }

        let window = unsafe { XRootWindow(display, 0) };

        Enigo {
            display: display,
            window: window,
        }
    }

    // TODO(dustin): implement drop
}

impl Default for Enigo {
    fn default() -> Self {
        Self::new()
    }
}

impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, self.window, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_move_relative(&mut self, x: i32, y: i32) {

        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, 0, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_down(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                1,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_up(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                0,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_click(&mut self, button: MouseButton) {
        use std::{thread, time};

        self.mouse_down(button);
        thread::sleep(time::Duration::from_millis(100));
        self.mouse_up(button);
    }

    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }

    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }
}

impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        for c in sequence.chars() {
            let rust_unicode: String = format!("U{:x}", c as u32);
            let keycode = self.unicode_string_to_keycode(&rust_unicode);

            self.keycode_click(*keycode);
        }
    }

    fn key_click(&mut self, key: Key) {
        self.keycode_click(self.key_to_keycode(key));
    }

    fn key_down(&mut self, key: Key) {
        self.keycode_down(self.key_to_keycode(key));
    }

    fn key_up(&mut self, key: Key) {
        self.keycode_up(self.key_to_keycode(key));
    }
}

struct KeyGuard<'a>(&'a Enigo, u32);

impl<'a> Deref for KeyGuard<'a> {
	type Target = u32;

	fn deref(&self) -> &Self::Target {
		return &self.1;
	}
}
impl<'a> Drop for KeyGuard<'a> {
	fn drop(&mut self) {
		self.0.reset_keycode(self.1);
	}
}

impl Enigo {
    fn reset_keycode(&self, keycode: u32) {
        unsafe {
            let keysym_list = [0 as KeySym, 0 as KeySym].as_ptr();
            XChangeKeyboardMapping(self.display, keycode as i32, 2, keysym_list, 1);
        }
    }

    fn unicode_string_to_keycode(&self, unicode_string: &str) -> KeyGuard {
        let mut keysyms_per_keycode = 0;
        // scratch space for temporary keycode bindings
        let mut scratch_keycode = 0;
        let mut keycode_low = 0;
        let mut keycode_high = 0;

        let keysyms = unsafe {
            // get the range of keycodes usually from 8 - 255
            XDisplayKeycodes(self.display, &mut keycode_low, &mut keycode_high);
            // get all the mapped keysysms available
            let keycode_count = keycode_high - keycode_low;
            XGetKeyboardMapping(
                self.display,
                keycode_low as u32,
                keycode_count,
                &mut keysyms_per_keycode,
            )
        };

        // find unused keycode for unmapped keysyms so we can
        // hook up our own keycode and map every keysym on it
        // so we just need to 'click' our once unmapped keycode
        for cidx in keycode_low..keycode_high + 1 {
            let mut key_is_empty = true;
            for sidx in 0..keysyms_per_keycode {
                let map_idx = (cidx - keycode_low) * keysyms_per_keycode + sidx;
                let sym_at_idx = unsafe { keysyms.offset(map_idx as isize) };
                if unsafe { *sym_at_idx } != 0 as *const c_void {
                    key_is_empty = false;
                } else {
                    break;
                }
            }
            if key_is_empty {
                scratch_keycode = cidx;
                break;
            }
        }

        unsafe {
            XFree(keysyms as *mut c_void);
            XFlush(self.display);
        }

        // TODO(dustin) make this an error!
        if scratch_keycode == 0 {
            panic!("cannot find free keycode");
        }

        // find the keysym for the given unicode char
        // map that keysym to our previous unmapped keycode
        // click that keycode/'button' with our keysym on it
        let unicode_as_c_string = CString::new(unicode_string).unwrap();
        let keysym = unsafe { XStringToKeysym(unicode_as_c_string.as_ptr() as *mut c_char) };
        let keysym_list = [keysym, keysym];
        let keysym_list = keysym_list.as_ptr();
        unsafe {
            XChangeKeyboardMapping(self.display, scratch_keycode, 2, keysym_list, 1);
            XFlush(self.display);
        }

        KeyGuard(self, scratch_keycode as u32)
    }

    fn key_to_keycode(&self, key: Key) -> u32 {
        unsafe {
            match key {
                Key::Return => XKeysymToKeycode(self.display, XK_RETURN as *const c_void, 0),
                Key::Tab => XKeysymToKeycode(self.display, XK_TAB as *const c_void, 0),
                Key::Space => XKeysymToKeycode(self.display, XK_SPACE as *const c_void, 0),
                Key::Backspace => XKeysymToKeycode(self.display, XK_BACKSPACE as *const c_void, 0),
                Key::Escape => XKeysymToKeycode(self.display, XK_ESCAPE as *const c_void, 0),
                Key::Super => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Command => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Windows => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Shift => XKeysymToKeycode(self.display, XK_SHIFT_L as *const c_void, 0),
                Key::CapsLock => XKeysymToKeycode(self.display, XK_CAPS_LOCK as *const c_void, 0),
                Key::Alt => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Option => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Control => XKeysymToKeycode(self.display, XK_CONTROL_L as *const c_void, 0),
                Key::Home => XKeysymToKeycode(self.display, XK_HOME as *const c_void, 0),
                Key::PageUp => XKeysymToKeycode(self.display, XK_PAGE_UP as *const c_void, 0),
                Key::PageDown => XKeysymToKeycode(self.display, XK_PAGE_DOWN as *const c_void, 0),
                Key::LeftArrow => XKeysymToKeycode(self.display, XK_LEFT as *const c_void, 0),
                Key::RightArrow => XKeysymToKeycode(self.display, XK_RIGHT as *const c_void, 0),
                Key::DownArrow => XKeysymToKeycode(self.display, XK_DOWN as *const c_void, 0),
                Key::UpArrow => XKeysymToKeycode(self.display, XK_UP as *const c_void, 0),
                Key::F1 => XKeysymToKeycode(self.display, XK_F1 as *const c_void, 0),
                Key::F2 => XKeysymToKeycode(self.display, XK_F2 as *const c_void, 0),
                Key::F3 => XKeysymToKeycode(self.display, XK_F3 as *const c_void, 0),
                Key::F4 => XKeysymToKeycode(self.display, XK_F4 as *const c_void, 0),
                Key::F5 => XKeysymToKeycode(self.display, XK_F5 as *const c_void, 0),
                Key::F6 => XKeysymToKeycode(self.display, XK_F6 as *const c_void, 0),
                Key::F7 => XKeysymToKeycode(self.display, XK_F7 as *const c_void, 0),
                Key::F8 => XKeysymToKeycode(self.display, XK_F8 as *const c_void, 0),
                Key::F9 => XKeysymToKeycode(self.display, XK_F9 as *const c_void, 0),
                Key::F10 => XKeysymToKeycode(self.display, XK_F10 as *const c_void, 0),
                Key::F11 => XKeysymToKeycode(self.display, XK_F11 as *const c_void, 0),
                Key::F12 => XKeysymToKeycode(self.display, XK_F12 as *const c_void, 0),

                Key::Raw(raw_keycode) => raw_keycode as u32,
                Key::Layout(c) => self.get_layoutdependent_keycode(c.to_string()),
            }
        }
    }

    fn get_layoutdependent_keycode(&self, string: String) -> u32 {
        let c_string = CString::new(string).unwrap();
        let keysym = unsafe { XStringToKeysym(c_string.as_ptr() as *mut c_char) };

        unsafe { XKeysymToKeycode(self.display, keysym, 0) }
    }

    fn keycode_click(&self, keycode: u32) {
        self.keycode_down(keycode);
        self.keycode_up(keycode);
    }

    fn keycode_down(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 1, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }

    fn keycode_up(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(10));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 0, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(10));
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use libc::{
    c_char,
    c_int,
};

use types::{
    CLPK_complex,
    CLPK_doublecomplex,
    CLPK_doublereal,
    CLPK_integer,
    CLPK_real,
};

#[link(name = "lapack")]
extern "C" {
}
<commit_msg>Add Syev trait<commit_after>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use libc::{
    c_char,
    c_int,
};

use types::{
    CLPK_complex,
    CLPK_doublecomplex,
    CLPK_doublereal,
    CLPK_integer,
    CLPK_real,
};

#[link(name = "lapack")]
extern "C" {
    pub fn ssyev_(jobz: c_char, uplo: c_char, n: *const CLPK_integer, a: *mut CLPK_real,
            lda: *const CLPK_integer, w: CLPK_real, work: CLPK_real,
            lwork: CLPK_integer,
            info: *mut CLPK_integer) -> c_int;
    pub fn dsyev_(jobz: c_char, uplo: c_char, n: *const CLPK_integer,
            a: *mut CLPK_doublereal, lda: *const CLPK_integer, w: CLPK_doublereal,
            work: CLPK_doublereal, lwork: CLPK_integer,
            info: *mut CLPK_integer) -> c_int;
}
<|endoftext|>


<commit_before>//!
//!	This module is responsible for reading mappability information produced using tallymer.
//!
use std::io::prelude::*;
use std::ffi::OsString;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Lines;
use std::collections::BTreeMap;
use std::mem::swap;

/// Specifies if a position is unmappable in each strand.
#[derive(Copy, Clone)]
pub struct UnMapPosition {
    pub plus: bool,
    pub minus: bool,
}

pub struct UnMap<R: BufRead> {
    data: BTreeMap<u64, UnMapPosition>, // map from position to pair (unmappable in plus, unmappable in minus)
    data_next: BTreeMap<u64, UnMapPosition>, // will contain the data for the next sequence, if any
    lines: Lines<R>,
    seqnumber: u64,
    next_seqnumber: u64,
}

fn parse_line(bytes: Vec<u8>) -> Result<(u64, u64, bool)> {
    let mut seq_idx = 0u64;
    let mut pos = 0u64;
    let mut idx = 0;

    while bytes[idx] >= b'0' && bytes[idx] <= b'9' {
        seq_idx = seq_idx * 10 + (bytes[idx] - b'0') as u64;
        idx += 1;
    }

    if bytes[idx] != b'\t' {
        return Err(Error::new(ErrorKind::Other, "unexpected byte in tallymer line"));
    }

    // parse sign
    idx += 1;
    let is_minus = match bytes[idx] {
        b'-' => true,
        b'+' => false,
        _ => return Err(Error::new(ErrorKind::Other, "unexpected byte in tallymer line"))
    };
    idx += 1;

    // parse position
    while idx < bytes.len() && bytes[idx] >= b'0' && bytes[idx] <= b'9' {
        pos = pos * 10 + (bytes[idx] - b'0') as u64;
        idx += 1;
    }

    Ok((seq_idx, pos, is_minus))
}

impl<R: BufRead> UnMap<R> {

	/// Open mappability file, pre-loading data for the first sequence
	pub fn open(reader: R) -> Result<UnMap<R>> {
        let mut result = UnMap {
            data: BTreeMap::new(),
            data_next: BTreeMap::new(),
            lines: reader.lines(),
            seqnumber: 0,
            next_seqnumber: 0,
        };
        try!(result.read_sequence_data());

        return Ok(result);
	}

    fn insert_value(map: &mut BTreeMap<u64, UnMapPosition>, pos: u64, is_minus: bool) {
        if let Some(entry) = map.get_mut(&pos) {
            if is_minus {
                entry.minus = true;
            } else {
                entry.plus = true;
            }
            return;
        }

        // else (not written as an else to satisfy the borrow checker!)
        map.insert(pos, UnMapPosition { plus: !is_minus, minus: is_minus });
    }

    fn read_sequence_data(&mut self) -> Result<()> {
        // read until either no more data or if find a row for the next sequence
        while let Some(line) = self.lines.next() {
            let line = try!(line);
            let (seq, pos, is_minus) = try!(parse_line(line.into_bytes()));

            if seq == self.seqnumber {
                Self::insert_value(&mut self.data, pos, is_minus);
            } else {
                Self::insert_value(&mut self.data_next, pos, is_minus);
                self.next_seqnumber = seq;
                break;
            }
        }
        Ok(())
    }

	/// Read the information for the next sequence from disk
	pub fn read_next_sequence(&mut self) -> Result<()> {
        // if we haven't reached the next sequence number yet
		if self.next_seqnumber > self.seqnumber + 1 {
            self.seqnumber = self.seqnumber + 1;
            self.data.clear();
            return Ok(());
        } else if self.seqnumber == self.next_seqnumber {
            // there is no more data to read
            self.data.clear();
            return Ok(());
        }
        self.seqnumber = self.next_seqnumber;

        // prepare
        swap(&mut self.data, &mut self.data_next);
        self.data_next.clear();

        // read until either no more data or if find a row for the next sequence
        try!(self.read_sequence_data());

        Ok(())
    }

    /// Query position
    ///
    /// Returns a pair of boolean values, which are true if the plus or minus strand read, respectively, is mappable at that position.
    pub fn is_unmappable(&self, position: u64) -> UnMapPosition {
        match self.data.get(&position) {
            Some(&result) => result,
            None => UnMapPosition { plus: false, minus: false }, // if not in map, then it's not unmappable
        }
    }
}
<commit_msg>Remove unused import<commit_after>//!
//!	This module is responsible for reading mappability information produced using tallymer.
//!
use std::io::prelude::*;
use std::io::Result;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Lines;
use std::collections::BTreeMap;
use std::mem::swap;

/// Specifies if a position is unmappable in each strand.
#[derive(Copy, Clone)]
pub struct UnMapPosition {
    pub plus: bool,
    pub minus: bool,
}

pub struct UnMap<R: BufRead> {
    data: BTreeMap<u64, UnMapPosition>, // map from position to pair (unmappable in plus, unmappable in minus)
    data_next: BTreeMap<u64, UnMapPosition>, // will contain the data for the next sequence, if any
    lines: Lines<R>,
    seqnumber: u64,
    next_seqnumber: u64,
}

fn parse_line(bytes: Vec<u8>) -> Result<(u64, u64, bool)> {
    let mut seq_idx = 0u64;
    let mut pos = 0u64;
    let mut idx = 0;

    while bytes[idx] >= b'0' && bytes[idx] <= b'9' {
        seq_idx = seq_idx * 10 + (bytes[idx] - b'0') as u64;
        idx += 1;
    }

    if bytes[idx] != b'\t' {
        return Err(Error::new(ErrorKind::Other, "unexpected byte in tallymer line"));
    }

    // parse sign
    idx += 1;
    let is_minus = match bytes[idx] {
        b'-' => true,
        b'+' => false,
        _ => return Err(Error::new(ErrorKind::Other, "unexpected byte in tallymer line"))
    };
    idx += 1;

    // parse position
    while idx < bytes.len() && bytes[idx] >= b'0' && bytes[idx] <= b'9' {
        pos = pos * 10 + (bytes[idx] - b'0') as u64;
        idx += 1;
    }

    Ok((seq_idx, pos, is_minus))
}

impl<R: BufRead> UnMap<R> {

	/// Open mappability file, pre-loading data for the first sequence
	pub fn open(reader: R) -> Result<UnMap<R>> {
        let mut result = UnMap {
            data: BTreeMap::new(),
            data_next: BTreeMap::new(),
            lines: reader.lines(),
            seqnumber: 0,
            next_seqnumber: 0,
        };
        try!(result.read_sequence_data());

        return Ok(result);
	}

    fn insert_value(map: &mut BTreeMap<u64, UnMapPosition>, pos: u64, is_minus: bool) {
        if let Some(entry) = map.get_mut(&pos) {
            if is_minus {
                entry.minus = true;
            } else {
                entry.plus = true;
            }
            return;
        }

        // else (not written as an else to satisfy the borrow checker!)
        map.insert(pos, UnMapPosition { plus: !is_minus, minus: is_minus });
    }

    fn read_sequence_data(&mut self) -> Result<()> {
        // read until either no more data or if find a row for the next sequence
        while let Some(line) = self.lines.next() {
            let line = try!(line);
            let (seq, pos, is_minus) = try!(parse_line(line.into_bytes()));

            if seq == self.seqnumber {
                Self::insert_value(&mut self.data, pos, is_minus);
            } else {
                Self::insert_value(&mut self.data_next, pos, is_minus);
                self.next_seqnumber = seq;
                break;
            }
        }
        Ok(())
    }

	/// Read the information for the next sequence from disk
	pub fn read_next_sequence(&mut self) -> Result<()> {
        // if we haven't reached the next sequence number yet
		if self.next_seqnumber > self.seqnumber + 1 {
            self.seqnumber = self.seqnumber + 1;
            self.data.clear();
            return Ok(());
        } else if self.seqnumber == self.next_seqnumber {
            // there is no more data to read
            self.data.clear();
            return Ok(());
        }
        self.seqnumber = self.next_seqnumber;

        // prepare
        swap(&mut self.data, &mut self.data_next);
        self.data_next.clear();

        // read until either no more data or if find a row for the next sequence
        try!(self.read_sequence_data());

        Ok(())
    }

    /// Query position
    ///
    /// Returns a pair of boolean values, which are true if the plus or minus strand read, respectively, is mappable at that position.
    pub fn is_unmappable(&self, position: u64) -> UnMapPosition {
        match self.data.get(&position) {
            Some(&result) => result,
            None => UnMapPosition { plus: false, minus: false }, // if not in map, then it's not unmappable
        }
    }
}
<|endoftext|>