<commit_before>extern crate cargo_tarpaulin;
extern crate nix;
extern crate gimli;
extern crate object;
extern crate memmap;
extern crate fallible_iterator;
extern crate rustc_demangle;
#[macro_use]
extern crate clap;

use std::path::Path;
use clap::{App, Arg, SubCommand, ArgSettings};
use cargo_tarpaulin::run;
use cargo_tarpaulin::config::*;

fn is_dir(d: String) -> Result<(), String> {
    if Path::new(&d).is_dir() {
        Ok(())
    } else {
        Err(String::from("root must be a directory"))
    }
}

const CI_SERVER_HELP: &'static str =
"Name of service, supported services are:
travis-ci, travis-pro, circle-ci, semaphore, jenkins and codeship.
If you are interfacing with coveralls.io or another site you can \
also specify a name that they will recognise. Refer to their documentation for this.";

fn main() {
    let args = App::new("cargo-tarpaulin")
        .author("Daniel McKenna, <danielmckenna93@gmail.com>")
        .about("Tool to analyse test coverage of cargo projects")
        .version(concat!("version: ", crate_version!()))
        .bin_name("cargo")
        .subcommand(SubCommand::with_name("tarpaulin")
            .about("Tool to analyse test coverage of cargo projects")
            .version(concat!("version: ", crate_version!()))
            .args_from_usage(
                "--help       'Prints help information'
                 --verbose -v 'Show extra output'
                 --ignore-tests 'ignore lines of test functions when collecting coverage'
                 --no-count   'Disables counting line hits for a faster run'
                 --ignored -i 'Run ignored tests as well'
                 --line -l    'Line coverage'
                 --skip-clean 'Skips the clean stage to reduce build times, may affect coverage results'
                 --branch -b  'Branch coverage: NOT IMPLEMENTED'
                 --forward -f 'Forwards unexpected signals to test. Tarpaulin will still take signals it is expecting.'
                 --coveralls [KEY]  'Coveralls key, either the repo token, or if you're using travis use $TRAVIS_JOB_ID and specify travis-{ci|pro} in --ciserver'
                 --features [FEATURE]... 'Features to be included in the target project'
                 --all        'Build all packages in the workspace'
                 --packages -p [PACKAGE]... 'Package id specifications for which package should be build. See cargo help pkgid for more info'
                 --exclude -e [PACKAGE]... 'Package id specifications to exclude from coverage. See cargo help pkgid for more info'")
            .args(&[
                Arg::from_usage("--out -o [FMT]   'Output path'")
                    .possible_values(&OutputFile::variants())
                    .multiple(true),
                Arg::from_usage("--root -r [DIR]  'Root directory containing Cargo.toml to use'")
                    .validator(is_dir),
                Arg::from_usage("--ciserver [SERVICE] 'CI server being used'")
                    .help(CI_SERVER_HELP),
                Arg::with_name("args")
                    .set(ArgSettings::Last)
                    .multiple(true)
            ]))
        .get_matches();

    let args = args.subcommand_matches("tarpaulin").unwrap_or(&args);
    let config = Config::from_args(args);
    match run(config) {
        Ok(()) => println!("Tarpaulin finished"),
        Err(e) => {
            println!("Error during run");
            std::process::exit(e);
        },
    }
}
<commit_msg>Removed explicit help text<commit_after>extern crate cargo_tarpaulin;
extern crate nix;
extern crate gimli;
extern crate object;
extern crate memmap;
extern crate fallible_iterator;
extern crate rustc_demangle;
#[macro_use]
extern crate clap;

use std::path::Path;
use clap::{App, Arg, SubCommand, ArgSettings};
use cargo_tarpaulin::run;
use cargo_tarpaulin::config::*;

fn is_dir(d: String) -> Result<(), String> {
    if Path::new(&d).is_dir() {
        Ok(())
    } else {
        Err(String::from("root must be a directory"))
    }
}

const CI_SERVER_HELP: &'static str =
"Name of service, supported services are:
travis-ci, travis-pro, circle-ci, semaphore, jenkins and codeship.
If you are interfacing with coveralls.io or another site you can \
also specify a name that they will recognise. Refer to their documentation for this.";

fn main() {
    let args = App::new("cargo-tarpaulin")
        .author("Daniel McKenna, <danielmckenna93@gmail.com>")
        .about("Tool to analyse test coverage of cargo projects")
        .version(concat!("version: ", crate_version!()))
        .bin_name("cargo")
        .subcommand(SubCommand::with_name("tarpaulin")
            .about("Tool to analyse test coverage of cargo projects")
            .version(concat!("version: ", crate_version!()))
            .args_from_usage(
                 "--verbose -v 'Show extra output'
                 --ignore-tests 'ignore lines of test functions when collecting coverage'
                 --no-count   'Disables counting line hits for a faster run'
                 --ignored -i 'Run ignored tests as well'
                 --line -l    'Line coverage'
                 --skip-clean 'Skips the clean stage to reduce build times, may affect coverage results'
                 --branch -b  'Branch coverage: NOT IMPLEMENTED'
                 --forward -f 'Forwards unexpected signals to test. Tarpaulin will still take signals it is expecting.'
                 --coveralls [KEY]  'Coveralls key, either the repo token, or if you're using travis use $TRAVIS_JOB_ID and specify travis-{ci|pro} in --ciserver'
                 --features [FEATURE]... 'Features to be included in the target project'
                 --all        'Build all packages in the workspace'
                 --packages -p [PACKAGE]... 'Package id specifications for which package should be build. See cargo help pkgid for more info'
                 --exclude -e [PACKAGE]... 'Package id specifications to exclude from coverage. See cargo help pkgid for more info'")
            .args(&[
                Arg::from_usage("--out -o [FMT]   'Output path'")
                    .possible_values(&OutputFile::variants())
                    .multiple(true),
                Arg::from_usage("--root -r [DIR]  'Root directory containing Cargo.toml to use'")
                    .validator(is_dir),
                Arg::from_usage("--ciserver [SERVICE] 'CI server being used'")
                    .help(CI_SERVER_HELP),
                Arg::with_name("args")
                    .set(ArgSettings::Last)
                    .multiple(true)
            ]))
        .get_matches();

    let args = args.subcommand_matches("tarpaulin").unwrap_or(&args);
    let config = Config::from_args(args);
    match run(config) {
        Ok(()) => println!("Tarpaulin finished"),
        Err(e) => {
            println!("Error during run");
            std::process::exit(e);
        },
    }
}
<|endoftext|>


<commit_before>
#[derive(Debug,Copy,Clone)]
enum Raw {
  // Most numbers will be the integer type in this language.
  Integer(i64),

  // Instead of floats, this language will have a rational type.
  Rational(i64, i64),
}

type Value = Result<Raw, &'static str>;

fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

fn new_integer(n: i64) -> Value {
    Ok(Raw::Integer(n))
}

fn new_rational(mut n: i64, mut d: i64) -> Value {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Ok(Raw::Integer(n))
    }
    else {
        Ok(Raw::Rational(n, d))
    }
}

fn divide(left: Value, right: Value) -> Value {
    match (try!(left), try!(right)) {

        // Implement int + int
        (Raw::Integer(a), Raw::Integer(b)) => new_rational(a, b),

        // The rest need implementing
        _ => Err("TODO: Implement more divide types"),
    }
}

fn main() {
    let a = new_integer(44);
    let b = new_integer(14);
    let c = divide(a, b);

    println!("{:?} / {:?} = {:?}", a, b, c);
    println!("20/5 = {:?}", new_rational(20, 5));
    println!("5/20 = {:?}", new_rational(5, 20));
    println!("20/-5 = {:?}", new_rational(20, -5));
    println!("5/-20 = {:?}", new_rational(5, -20));
    println!("5/-60 = {:?}", new_rational(5, -60));
    println!("120/-30- = {:?}", new_rational(120, -30));
    println!("-120/30- = {:?}", new_rational(-120, 30));
}
<commit_msg>Implement the rest<commit_after>
#[derive(Debug,Copy,Clone)]
enum Raw {
  // Numbers in this language can be simple integers or rational.
  // They are the same to the user and automatically switch to the best
  // type on every calculation.
  Integer(i64),
  Rational(i64, i64),
}

type Value = Result<Raw, &'static str>;

// Calculate the greatest common divisor using Euclid's algorithm.
fn gcd(mut a: i64, mut b: i64) -> i64 {
    let mut c;
    while a != 0 {
        c = a;
        a = b % a;
        b = c;
    }
    b
}

#[inline(always)]
fn new_integer(n: i64) -> Value {
    Ok(Raw::Integer(n))
}

// This normalizes inputs and sometimes even converts to integers.
fn new_rational(mut n: i64, mut d: i64) -> Value {
    let g = gcd(n, d);
    n = n / g;
    d = d / g;
    if d < 0 {
      n = -n;
      d = -d;
    }
    if d == 1 {
        Ok(Raw::Integer(n))
    }
    else {
        Ok(Raw::Rational(n, d))
    }
}

// We could just let new_rational handle the gcd here, but this
// avoids overflows by dividing the gcd in first.
fn rational_add(n1: i64, d1: i64, n2: i64, d2: i64) -> Value {
  if d1 == d2 {
    // Fast path for common divisors.
    new_rational(n1 + n2, d1)
  }
  else {
    let g = gcd(d1, d2);
    new_rational(n1 * (d2 / g) + n2 * (d1 / g), d1 / g * d2)
  }
}

fn add(left: Value, right: Value) -> Value {
    match (try!(left), try!(right)) {
        (Raw::Integer(a), Raw::Integer(b)) => new_integer(a + b),
        (Raw::Rational(n, d), Raw::Integer(b)) => rational_add(n, d, b, 1),
        (Raw::Integer(a), Raw::Rational(n, d)) => rational_add(a, 1, n, d),
        (Raw::Rational(n1, d1), Raw::Rational(n2, d2)) => rational_add(n1, d1, n2, d2)
    }
}

fn subtract(left: Value, right: Value) -> Value {
    match (try!(left), try!(right)) {
        (Raw::Integer(a), Raw::Integer(b)) => new_integer(a - b),
        (Raw::Rational(n, d), Raw::Integer(b)) => rational_add(n, d, -b, 1),
        (Raw::Integer(a), Raw::Rational(n, d)) => rational_add(a, 1, -n, d),
        (Raw::Rational(n1, d1), Raw::Rational(n2, d2)) => rational_add(n1, d1, -n2, d2)
    }
}

fn divide(left: Value, right: Value) -> Value {
    match (try!(left), try!(right)) {
        (Raw::Integer(a), Raw::Integer(b)) => new_rational(a, b),
        (Raw::Rational(n, d), Raw::Integer(b)) => new_rational(n, d * b),
        (Raw::Integer(a), Raw::Rational(n, d)) => new_rational(a * d, n),
        (Raw::Rational(n1, d1), Raw::Rational(n2, d2)) => new_rational(n1 * d2, d1 * n2),
    }
}

fn multiply(left: Value, right: Value) -> Value {
    match (try!(left), try!(right)) {
        (Raw::Integer(a), Raw::Integer(b)) => new_integer(a * b),
        (Raw::Rational(n, d), Raw::Integer(b)) => new_rational(n * b, d),
        (Raw::Integer(a), Raw::Rational(n, d)) => new_rational(a * n, d),
        (Raw::Rational(n1, d1), Raw::Rational(n2, d2)) => new_rational(n1 * n2, d1 * d2),
    }
}

fn test(a: Value, b: Value) {
    println!("{:?} / {:?} = {:?}", a, b, divide(a, b));
    println!("{:?} * {:?} = {:?}", a, b, multiply(a, b));
    println!("{:?} + {:?} = {:?}", a, b, add(a, b));
    println!("{:?} - {:?} = {:?}", a, b, subtract(a, b));
}

fn main() {
    test(new_integer(44), new_integer(14));
    test(new_integer(14), new_integer(44));
    test(new_integer(44), new_integer(-14));
    test(new_integer(14), new_integer(-44));
    test(new_integer(-44), new_integer(14));
    test(new_integer(-14), new_integer(44));
    test(new_integer(-44), new_integer(-14));
    test(new_integer(-14), new_integer(-44));
    test(new_rational(1, 2), new_rational(2, 1));
    test(new_rational(2, 1), new_rational(1, 2));
    test(new_rational(1, 2), new_rational(1, 2));
    test(new_rational(2, 2), new_rational(2, 1));
}
<|endoftext|>


<commit_before>#![deny(
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unused_import_braces,
    unused_qualifications
)]

#[macro_use]
extern crate clap;
extern crate base64;
extern crate bytes;
extern crate md5;
extern crate pbr;
extern crate reqwest;
extern crate url;
extern crate uuid;
#[macro_use]
extern crate lazy_static;

mod auth_helper;
mod file_helper;
mod request_helper;
mod ui_helper;

use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use clap::App;
use reqwest::header::{AcceptRanges, ContentLength, RangeUnit};
use reqwest::Url;
use std::ops::Deref;
use std::path::Path;
use std::time::Duration;
use std::{process, thread};

const VERSION: &str = env!("CARGO_PKG_VERSION");

lazy_static! {
    static ref CURRENTLY_RUNNING_THREADS: AtomicUsize = AtomicUsize::new(0);
    static ref HAS_FAILED: AtomicBool = AtomicBool::new(false);
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let m = App::from_yaml(yaml).version(VERSION).get_matches();

    #[cfg_attr(feature = "clippy", allow(option_unwrap_used))]
    let raw_uri = m.value_of("uri").unwrap(); // Unwrap is safe - required by clap
    let url = match Url::parse(raw_uri) {
        Ok(uri) => uri,
        Err(e) => panic!("Couldn't parse URI: {}", e),
    };

    let file_name = request_helper::get_last_url_segment_decoded(&url);

    if Path::new(&file_name).exists() {
        println!(
            "{} already exists, please remove it and try again.",
            file_name
        );
        process::exit(1);
    }

    let thread_count = m
        .value_of("thread_count")
        .map(|tc| tc.parse::<usize>().expect("Failed to parse thread count."))
        .unwrap_or(10);
    let part_count = m
        .value_of("part_count")
        .map(|tc| tc.parse::<usize>().expect("Failed to parse part count."))
        .unwrap_or(thread_count);
    let part_count_u64 = part_count as u64;

    if part_count < thread_count {
        panic!("Part count too low, must be at least the thread count.");
    }

    if thread_count > 20 {
        panic!("Thread count too high, please select between 2 and 20 threads.");
    } else if thread_count < 2 {
        panic!("Thread count too low, please select between 2 and 20 threads.");
    }

    let res = request_helper::head_request(url.clone());
    let headers = res.headers();
    let has_range_header = headers.get::<AcceptRanges>().map_or(false, |range_header| {
        range_header.deref().contains(&RangeUnit::Bytes)
    });
    if !has_range_header {
        panic!("Requested resource does not allow Range requests!")
    }

    let content_length = headers
        .get::<ContentLength>()
        .map_or(0, |length_header| *length_header.deref());

    if content_length < 1024 {
        panic!("Content too small");
    }

    let part_length = (content_length / part_count_u64) / file_helper::CHUNK_SIZE_U64
        * file_helper::CHUNK_SIZE_U64;

    let mut sections: Vec<(u64, u64)> = vec![];
    let mut lengths: Vec<u64> = vec![];
    for section in 0..(part_count_u64 - 1) {
        sections.push((section * part_length, (section + 1) * part_length - 1));
        lengths.push(part_length);
    }
    sections.push(((part_count_u64 - 1) * part_length, content_length - 1));
    lengths.push(content_length - (part_count_u64 - 1) * part_length);

    ui_helper::start_pbr(&file_name, lengths);

    let footer_space = file_helper::create_file(&file_name, content_length);
    let mut children = vec![];
    for (child_id, section) in sections.into_iter().enumerate() {
        let url_clone = url.clone();
        let file_name_clone = file_name.clone();
        loop {
            {
                let currently_running = CURRENTLY_RUNNING_THREADS.load(Ordering::AcqRel);
                if currently_running < thread_count {
                    CURRENTLY_RUNNING_THREADS.store(currently_running + 1, Ordering::AcqRel);
                    break;
                }
            }
            thread::sleep(Duration::new(1, 0));
        }
        let child = thread::spawn(move || {
            ui_helper::setting_up_bar(child_id);
            let start =
                file_helper::get_first_empty_chunk(&file_name_clone, footer_space, section);
            if start <= section.1 {
                let prefilled = start - section.0;
                let section = (start, section.1);
                let range_req = request_helper::get_range_request(url_clone.clone(), section);
                ui_helper::start_bar(child_id);
                let written = file_helper::save_response(
                    &file_name_clone,
                    range_req,
                    footer_space,
                    child_id,
                    prefilled,
                ).unwrap();
                if written > 0 {
                    ui_helper::success_bar(child_id);
                } else {
                    ui_helper::fail_bar(child_id);
                    HAS_FAILED.store(true, Ordering::AcqRel);
                }
            } else {
                ui_helper::update_bar(child_id, section.1 - section.0 + 1);
                ui_helper::success_bar(child_id);
            }

            CURRENTLY_RUNNING_THREADS.store(CURRENTLY_RUNNING_THREADS.load(Ordering::AcqRel) - 1, Ordering::AcqRel);
        });
        children.push(child);
    }
    for child in children {
        let _ = child.join();
    }

    if HAS_FAILED.load(Ordering::AcqRel) {
        println!("Some parts failed to download, please rerun.");
        process::exit(1);
    } else {
        ui_helper::success_global_bar();
        file_helper::remove_footer_and_save(&file_name, content_length);
    }
}
<commit_msg>Fix incorrect use of AcqRel ordering<commit_after>#![deny(
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unused_import_braces,
    unused_qualifications
)]

#[macro_use]
extern crate clap;
extern crate base64;
extern crate bytes;
extern crate md5;
extern crate pbr;
extern crate reqwest;
extern crate url;
extern crate uuid;
#[macro_use]
extern crate lazy_static;

mod auth_helper;
mod file_helper;
mod request_helper;
mod ui_helper;

use clap::App;
use reqwest::header::{AcceptRanges, ContentLength, RangeUnit};
use reqwest::Url;
use std::ops::Deref;
use std::path::Path;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::time::Duration;
use std::{process, thread};

const VERSION: &str = env!("CARGO_PKG_VERSION");

lazy_static! {
    static ref CURRENTLY_RUNNING_THREADS: AtomicUsize = AtomicUsize::new(0);
    static ref HAS_FAILED: AtomicBool = AtomicBool::new(false);
}

fn main() {
    let yaml = load_yaml!("cli.yml");
    let m = App::from_yaml(yaml).version(VERSION).get_matches();

    #[cfg_attr(feature = "clippy", allow(option_unwrap_used))]
    let raw_uri = m.value_of("uri").unwrap(); // Unwrap is safe - required by clap
    let url = match Url::parse(raw_uri) {
        Ok(uri) => uri,
        Err(e) => panic!("Couldn't parse URI: {}", e),
    };

    let file_name = request_helper::get_last_url_segment_decoded(&url);

    if Path::new(&file_name).exists() {
        println!(
            "{} already exists, please remove it and try again.",
            file_name
        );
        process::exit(1);
    }

    let thread_count = m
        .value_of("thread_count")
        .map(|tc| tc.parse::<usize>().expect("Failed to parse thread count."))
        .unwrap_or(10);
    let part_count = m
        .value_of("part_count")
        .map(|tc| tc.parse::<usize>().expect("Failed to parse part count."))
        .unwrap_or(thread_count);
    let part_count_u64 = part_count as u64;

    if part_count < thread_count {
        panic!("Part count too low, must be at least the thread count.");
    }

    if thread_count > 20 {
        panic!("Thread count too high, please select between 2 and 20 threads.");
    } else if thread_count < 2 {
        panic!("Thread count too low, please select between 2 and 20 threads.");
    }

    let res = request_helper::head_request(url.clone());
    let headers = res.headers();
    let has_range_header = headers.get::<AcceptRanges>().map_or(false, |range_header| {
        range_header.deref().contains(&RangeUnit::Bytes)
    });
    if !has_range_header {
        panic!("Requested resource does not allow Range requests!")
    }

    let content_length = headers
        .get::<ContentLength>()
        .map_or(0, |length_header| *length_header.deref());

    if content_length < 1024 {
        panic!("Content too small");
    }

    let part_length = (content_length / part_count_u64) / file_helper::CHUNK_SIZE_U64
        * file_helper::CHUNK_SIZE_U64;

    let mut sections: Vec<(u64, u64)> = vec![];
    let mut lengths: Vec<u64> = vec![];
    for section in 0..(part_count_u64 - 1) {
        sections.push((section * part_length, (section + 1) * part_length - 1));
        lengths.push(part_length);
    }
    sections.push(((part_count_u64 - 1) * part_length, content_length - 1));
    lengths.push(content_length - (part_count_u64 - 1) * part_length);

    ui_helper::start_pbr(&file_name, lengths);

    let footer_space = file_helper::create_file(&file_name, content_length);
    let mut children = vec![];
    for (child_id, section) in sections.into_iter().enumerate() {
        let url_clone = url.clone();
        let file_name_clone = file_name.clone();
        loop {
            {
                let currently_running = CURRENTLY_RUNNING_THREADS.load(Ordering::Acquire);
                if currently_running < thread_count {
                    CURRENTLY_RUNNING_THREADS.store(currently_running + 1, Ordering::Release);
                    break;
                }
            }
            thread::sleep(Duration::new(1, 0));
        }
        let child = thread::spawn(move || {
            ui_helper::setting_up_bar(child_id);
            let start = file_helper::get_first_empty_chunk(&file_name_clone, footer_space, section);
            if start <= section.1 {
                let prefilled = start - section.0;
                let section = (start, section.1);
                let range_req = request_helper::get_range_request(url_clone.clone(), section);
                ui_helper::start_bar(child_id);
                let written = file_helper::save_response(
                    &file_name_clone,
                    range_req,
                    footer_space,
                    child_id,
                    prefilled,
                ).unwrap();
                if written > 0 {
                    ui_helper::success_bar(child_id);
                } else {
                    ui_helper::fail_bar(child_id);
                    HAS_FAILED.store(true, Ordering::Release);
                }
            } else {
                ui_helper::update_bar(child_id, section.1 - section.0 + 1);
                ui_helper::success_bar(child_id);
            }

            CURRENTLY_RUNNING_THREADS.store(
                CURRENTLY_RUNNING_THREADS.load(Ordering::Acquire) - 1,
                Ordering::Release,
            );
        });
        children.push(child);
    }
    for child in children {
        let _ = child.join();
    }

    if HAS_FAILED.load(Ordering::Acquire) {
        println!("Some parts failed to download, please rerun.");
        process::exit(1);
    } else {
        ui_helper::success_global_bar();
        file_helper::remove_footer_and_save(&file_name, content_length);
    }
}
<|endoftext|>


<commit_before>use std::thread;

fn main() {

    for i in 1..10 {

        println!("current i is: {}", i);

        let temp1 = thread::spawn(move || {

            println!("spawn thread {}", i);
        });

        println!("thread begin {}", i);

        let temp2 = thread::Builder::new()
                    .name("thread".to_string())
                    .stack_size(1024 * 1024 * 5)
                    .spawn(move || {

                        println!("builder thread {}", i);
                    });

        temp1.join().unwrap();

        println!("thread process {}", i);

        temp2.unwrap().join().unwrap();

        println!("thread end {}", i);
    }

    println!("main end");
}<commit_msg>chore: test mpsc<commit_after>use std::thread;
use std::sync::mpsc;

fn main() {

    let (sender, recevier): (mpsc::Sender<i32>, mpsc::Receiver<i32>) = mpsc::channel();

    thread::spawn(move || {
        sender.send(32).unwrap();
    });

    let temp = recevier.recv();

    println!("revevie {}", temp.unwrap());
}<|endoftext|>


<commit_before>/*
 * Tool statically unpacking HotS binaries.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 athre0z
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

extern crate crypto;
mod pe;

use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::error::Error;
use std::mem;
use std::slice;
use std::env;

use crypto::{buffer, aes, blockmodes};

#[allow(unused_imports)]
use crypto::symmetriccipher::Encryptor;

/// Entry-point.
fn main() {
    println!("HotS-unpack by athre0z (c) 2015 zyantific.com\n");

    // Parse arguments.
    let args: Vec<_> = env::args().collect();
    if args.len() != 2 {
        println!("[*] Usage: {} <input binary>", args[0]);
        return;
    }

    // Read file.
    let mut f = match File::open(&Path::new(&args[1])) {
        Err(why) => panic!("[-] Couldn't open input file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    let mut data = Vec::new();
    let data_size = match f.read_to_end(&mut data) {
        Err(why) => panic!("[-] Couldn't read input file: {}", Error::description(&why)),
        Ok(x) => x
    };

    println!("[+] Read file: {} bytes", data_size);

    // Parse DOS-header.
    let mz = get_data::<pe::ImageDosHeader>(&mut data, 0);
    if unsafe { (*mz).magic } != pe::DOS_MAGIC {
        panic!("[-] Invalid input file: invalid DOS header");
    }

    // Parse NT-header.
    let nt_header_offs = unsafe { (*mz).lfanew } as usize;
    let nt = get_data::<pe::ImageNtHeaders32>(&mut data, nt_header_offs);
    if unsafe { (*nt).signature } != pe::PE_MAGIC {
        panic!("[-] Invalid input file: invalid NT header");
    }

    // Obtain useful information.
    let sec_table_offs = nt_header_offs + mem::size_of::<pe::ImageNtHeaders32>();
    let num_secs = unsafe { (*nt).file_header.number_of_sections } as usize;
    let data_offs = sec_table_offs
        + mem::size_of::<pe::ImageSectionHeader>() * num_secs;
    let image_base = unsafe { (*nt).opt_header.image_base } as usize;

    // Scan for chunk table.
    println!("[*] Heuristically locating chunk table ...");
    let mut chunk_table_offs = 0;
    let mut chunk_table_len = 0;

    'scan: for i in data_offs..data.len() {
        let mut offs = 0usize;
        let mut last_addr = 0u32;

        // Check if candidate looks like a chunk table.
        while i + offs + mem::size_of::<ChunkEntry>() <= data.len() {
            unsafe {
                let cur = get_data::<ChunkEntry>(&mut data, i + offs);

                // The chunk table's end is indicated with an 0xFFFFFFFF.
                if offs >= 1000 * mem::size_of::<ChunkEntry>() && (*cur).offset == 0xFFFFFFFF {
                    chunk_table_offs = i;
                    chunk_table_len = (offs / mem::size_of::<ChunkEntry>()) - 1;
                    break 'scan;
                }

                // The chunk table entries are stored ascending.
                if (*cur).offset <= last_addr {
                    break;
                }

                last_addr = (*cur).offset;
                offs += mem::size_of::<ChunkEntry>();
            }
        }
    }

    if chunk_table_offs == 0 || chunk_table_len == 0 {
        panic!("[-] Unable to locate chunk table.");
    }

    println!("[+] Found chunk table @ FO 0x{:x} ({} entries).",
        chunk_table_offs, chunk_table_len);

    // Scan for AES key.
    println!("[*] Locating AES key ...");

    // 0xFF -> wildcard
    let pattern = [
        0x68, 0x80, 0x00, 0x00, 0x00, // push 80h
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesInputKey
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesKey
        0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call AesSetDecryptKey
        0xE9,                         // jmp  XXX
    ];

    let mut match_ = None;
    for offs in sec_table_offs..(data.len() - pattern.len()) {
        let mut exhausted = true;
        for (i, cur_pat_byte) in pattern.iter().enumerate() {
            if *cur_pat_byte == 0xFF { continue; }
            if data[offs + i] != *cur_pat_byte {
                exhausted = false;
                break;
            }
        }

        if exhausted {
            match_ = Some(offs);
            break;
        }
    }

    let aes_key_rva = match match_ {
        Some(x) => unsafe { *get_data::<u32>(&mut data, x + 6) as usize - image_base },
        None => panic!("[-] Unable to locate AES key.")
    };

    print!("[+] Found AES key @ RVA 0x{:x}: ", aes_key_rva);

    // Dump AES key.
    let aes_key = unsafe { *get_data_rva::<[u8; 16]>(
        &mut data, sec_table_offs, num_secs, aes_key_rva) };

    for i in aes_key.iter() {
        print!("{:02x} ", i);
    }
    print!("\n");

    // Collect all encrypted chunks into one buffer (we got plenty of memory, don't we? :p)
    println!("[*] Merging encrypted chunks ...", );
    let mut encrypted_blob = Vec::<u8>::new();
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            encrypted_blob.extend(get_slice_rva::<u8>(
                &mut data, sec_table_offs, num_secs, (*chunk_info).offset as usize,
                (*chunk_info).size as usize).to_vec());
        }
    }

    let padding_length = 16 - encrypted_blob.len() % 16;
    if padding_length > 0 {
        encrypted_blob.extend(vec![0; padding_length]);
    }

    // Decrypt blob.
    println!("[*] Decrypting ...");
    let mut decryptor = aes::ecb_decryptor(
        aes::KeySize::KeySize128,
        &aes_key,
        blockmodes::NoPadding);
    let mut decrypted_blob = vec![0u8; encrypted_blob.len()];

    {
        let mut read_buf = buffer::RefReadBuffer::new(&encrypted_blob);
        let mut write_buf = buffer::RefWriteBuffer::new(&mut decrypted_blob);
        match decryptor.decrypt(&mut read_buf, &mut write_buf, true) {
            Err(why) => panic!("[-] Unable to decrypt data: {:?}", why),
            Ok(result) => match result {
                buffer::BufferResult::BufferUnderflow => {},
                buffer::BufferResult::BufferOverflow => unreachable!(),
            }
        }
    }

    // Write data from blob back into our file buffer.
    println!("[*] Putting decrypted data where it belongs ...");
    let mut read_offset = 0usize;
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            let chunk_size = (*chunk_info).size as usize;
            let mut dst = get_slice_rva::<u8>(&mut data, sec_table_offs, num_secs,
                (*chunk_info).offset as usize, chunk_size);
            let src = &decrypted_blob[read_offset..read_offset + chunk_size];

            for (cur_src_byte, cur_dst_byte) in src.iter().zip(dst.iter_mut()) {
                *cur_dst_byte = *cur_src_byte;
            }

            read_offset += chunk_size;
        }
    }

    // Blizz moved the IAT to the .reloc section, which isn't loaded by IDA
    // when loading a file without the "manual load" option, which results in
    // unresolved API calls. Rename it to make IDA map it into the IDB by default.
    println!("[*] Fixing section table ...");
    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            &mut data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            if (*cur_sec).name.iter().zip(".reloc".as_bytes().iter()).all(|(a, b)| a == b) {
                for (src, dst) in ".reloc_".as_bytes().iter().zip((*cur_sec).name.iter_mut()) {
                    *dst = *src;
                }
            }
        }
    }

    // Write output file.
    println!("[*] Writing output file ...");
    let mut out_f = match File::create(&Path::new("out.exe")) {
        Err(why) => panic!("[-] Couldn't open output file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    match out_f.write_all(&data) {
        Err(why) => panic!("[-] Couldn't write to output file: {}", Error::description(&why)),
        Ok(..) => println!("[+] Wrote {:?} bytes (out.exe).", data.len()),
    }
}

#[repr(C)]
#[packed]
struct ChunkEntry {
    pub offset: u32,
    pub size: u32,
}

/// Obtains a ptr to a struct inside the input file by it's file offset.
fn get_data<T>(data: &mut Vec<u8>, file_offset: usize) -> *mut T {
    if mem::size_of::<T>() > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }
    unsafe { mem::transmute(data.as_ptr() as usize + file_offset) }
}

/// Obtains a ptr to a struct inside the input file by it's RVA.
fn get_data_rva<T>(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> *mut T {

    match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(rva) => get_data::<T>(data, rva),
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    }
}

/// Obtains a mutable slice from the input file by it's RVA.
fn get_slice_rva<'a, T>(data: &'a mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize, len: usize) -> &'a mut [T] {

    let file_offset = match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(fo) => fo,
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    };

    if mem::size_of::<T>() * len > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }

    unsafe { slice::from_raw_parts_mut(mem::transmute(
        data.as_ptr() as usize + file_offset), len) }
}

/// Translates an RVA to a file offset.
fn rva_to_fo(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> Option<usize> {

    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            let sec_va = (*cur_sec).virtual_address as usize;
            if rva >= sec_va && rva <= sec_va + (*cur_sec).virtual_size as usize {
                return Some(rva - sec_va + (*cur_sec).pointer_to_raw_data as usize);
            }
        }
    }

    None
}<commit_msg>Added second argument for optional output file name.<commit_after>/*
 * Tool statically unpacking HotS binaries.
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 athre0z
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

extern crate crypto;
mod pe;

use std::fs::File;
use std::path::Path;
use std::io::prelude::*;
use std::error::Error;
use std::mem;
use std::slice;
use std::env;

use crypto::{buffer, aes, blockmodes};

#[allow(unused_imports)]
use crypto::symmetriccipher::Encryptor;

/// Entry-point.
fn main() {
    println!("HotS-unpack by athre0z (c) 2015 zyantific.com\n");

    // Parse arguments.
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 || args.len() > 3 {
        println!("[*] Usage: {} <input binary> [output binary]", args[0]);
        return;
    }

    // Read file.
    let mut f = match File::open(&Path::new(&args[1])) {
        Err(why) => panic!("[-] Couldn't open input file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    let out: String = match args.len() {
        3 => args[2].to_string(),
	_ => "out.exe".to_string()
    };

    let mut data = Vec::new();
    let data_size = match f.read_to_end(&mut data) {
        Err(why) => panic!("[-] Couldn't read input file: {}", Error::description(&why)),
        Ok(x) => x
    };

    println!("[+] Read file: {} bytes", data_size);

    // Parse DOS-header.
    let mz = get_data::<pe::ImageDosHeader>(&mut data, 0);
    if unsafe { (*mz).magic } != pe::DOS_MAGIC {
        panic!("[-] Invalid input file: invalid DOS header");
    }

    // Parse NT-header.
    let nt_header_offs = unsafe { (*mz).lfanew } as usize;
    let nt = get_data::<pe::ImageNtHeaders32>(&mut data, nt_header_offs);
    if unsafe { (*nt).signature } != pe::PE_MAGIC {
        panic!("[-] Invalid input file: invalid NT header");
    }

    // Obtain useful information.
    let sec_table_offs = nt_header_offs + mem::size_of::<pe::ImageNtHeaders32>();
    let num_secs = unsafe { (*nt).file_header.number_of_sections } as usize;
    let data_offs = sec_table_offs
        + mem::size_of::<pe::ImageSectionHeader>() * num_secs;
    let image_base = unsafe { (*nt).opt_header.image_base } as usize;

    // Scan for chunk table.
    println!("[*] Heuristically locating chunk table ...");
    let mut chunk_table_offs = 0;
    let mut chunk_table_len = 0;

    'scan: for i in data_offs..data.len() {
        let mut offs = 0usize;
        let mut last_addr = 0u32;

        // Check if candidate looks like a chunk table.
        while i + offs + mem::size_of::<ChunkEntry>() <= data.len() {
            unsafe {
                let cur = get_data::<ChunkEntry>(&mut data, i + offs);

                // The chunk table's end is indicated with an 0xFFFFFFFF.
                if offs >= 1000 * mem::size_of::<ChunkEntry>() && (*cur).offset == 0xFFFFFFFF {
                    chunk_table_offs = i;
                    chunk_table_len = (offs / mem::size_of::<ChunkEntry>()) - 1;
                    break 'scan;
                }

                // The chunk table entries are stored ascending.
                if (*cur).offset <= last_addr {
                    break;
                }

                last_addr = (*cur).offset;
                offs += mem::size_of::<ChunkEntry>();
            }
        }
    }

    if chunk_table_offs == 0 || chunk_table_len == 0 {
        panic!("[-] Unable to locate chunk table.");
    }

    println!("[+] Found chunk table @ FO 0x{:x} ({} entries).",
        chunk_table_offs, chunk_table_len);

    // Scan for AES key.
    println!("[*] Locating AES key ...");

    // 0xFF -> wildcard
    let pattern = [
        0x68, 0x80, 0x00, 0x00, 0x00, // push 80h
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesInputKey
        0x68, 0xFF, 0xFF, 0xFF, 0xFF, // push offset g_aesKey
        0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call AesSetDecryptKey
        0xE9,                         // jmp  XXX
    ];

    let mut match_ = None;
    for offs in sec_table_offs..(data.len() - pattern.len()) {
        let mut exhausted = true;
        for (i, cur_pat_byte) in pattern.iter().enumerate() {
            if *cur_pat_byte == 0xFF { continue; }
            if data[offs + i] != *cur_pat_byte {
                exhausted = false;
                break;
            }
        }

        if exhausted {
            match_ = Some(offs);
            break;
        }
    }

    let aes_key_rva = match match_ {
        Some(x) => unsafe { *get_data::<u32>(&mut data, x + 6) as usize - image_base },
        None => panic!("[-] Unable to locate AES key.")
    };

    print!("[+] Found AES key @ RVA 0x{:x}: ", aes_key_rva);

    // Dump AES key.
    let aes_key = unsafe { *get_data_rva::<[u8; 16]>(
        &mut data, sec_table_offs, num_secs, aes_key_rva) };

    for i in aes_key.iter() {
        print!("{:02x} ", i);
    }
    print!("\n");

    // Collect all encrypted chunks into one buffer (we got plenty of memory, don't we? :p)
    println!("[*] Merging encrypted chunks ...", );
    let mut encrypted_blob = Vec::<u8>::new();
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            encrypted_blob.extend(get_slice_rva::<u8>(
                &mut data, sec_table_offs, num_secs, (*chunk_info).offset as usize,
                (*chunk_info).size as usize).to_vec());
        }
    }

    let padding_length = 16 - encrypted_blob.len() % 16;
    if padding_length > 0 {
        encrypted_blob.extend(vec![0; padding_length]);
    }

    // Decrypt blob.
    println!("[*] Decrypting ...");
    let mut decryptor = aes::ecb_decryptor(
        aes::KeySize::KeySize128,
        &aes_key,
        blockmodes::NoPadding);
    let mut decrypted_blob = vec![0u8; encrypted_blob.len()];

    {
        let mut read_buf = buffer::RefReadBuffer::new(&encrypted_blob);
        let mut write_buf = buffer::RefWriteBuffer::new(&mut decrypted_blob);
        match decryptor.decrypt(&mut read_buf, &mut write_buf, true) {
            Err(why) => panic!("[-] Unable to decrypt data: {:?}", why),
            Ok(result) => match result {
                buffer::BufferResult::BufferUnderflow => {},
                buffer::BufferResult::BufferOverflow => unreachable!(),
            }
        }
    }

    // Write data from blob back into our file buffer.
    println!("[*] Putting decrypted data where it belongs ...");
    let mut read_offset = 0usize;
    for i in 0..chunk_table_len {
        let chunk_info = get_data::<ChunkEntry>(
            &mut data, chunk_table_offs + mem::size_of::<ChunkEntry>() * i);
        unsafe {
            let chunk_size = (*chunk_info).size as usize;
            let mut dst = get_slice_rva::<u8>(&mut data, sec_table_offs, num_secs,
                (*chunk_info).offset as usize, chunk_size);
            let src = &decrypted_blob[read_offset..read_offset + chunk_size];

            for (cur_src_byte, cur_dst_byte) in src.iter().zip(dst.iter_mut()) {
                *cur_dst_byte = *cur_src_byte;
            }

            read_offset += chunk_size;
        }
    }

    // Blizz moved the IAT to the .reloc section, which isn't loaded by IDA
    // when loading a file without the "manual load" option, which results in
    // unresolved API calls. Rename it to make IDA map it into the IDB by default.
    println!("[*] Fixing section table ...");
    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            &mut data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            if (*cur_sec).name.iter().zip(".reloc".as_bytes().iter()).all(|(a, b)| a == b) {
                for (src, dst) in ".reloc_".as_bytes().iter().zip((*cur_sec).name.iter_mut()) {
                    *dst = *src;
                }
            }
        }
    }

    // Write output file.
    println!("[*] Writing output file ...");
    let mut out_f = match File::create(&Path::new(&out)) {
        Err(why) => panic!("[-] Couldn't open output file: {}", Error::description(&why)),
        Ok(file) => file,
    };

    match out_f.write_all(&data) {
        Err(why) => panic!("[-] Couldn't write to output file: {}", Error::description(&why)),
        Ok(..) => println!("[+] Wrote {:?} bytes ({}).", data.len(), &out),
    }
}

#[repr(C)]
#[packed]
struct ChunkEntry {
    pub offset: u32,
    pub size: u32,
}

/// Obtains a ptr to a struct inside the input file by it's file offset.
fn get_data<T>(data: &mut Vec<u8>, file_offset: usize) -> *mut T {
    if mem::size_of::<T>() > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }
    unsafe { mem::transmute(data.as_ptr() as usize + file_offset) }
}

/// Obtains a ptr to a struct inside the input file by it's RVA.
fn get_data_rva<T>(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> *mut T {

    match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(rva) => get_data::<T>(data, rva),
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    }
}

/// Obtains a mutable slice from the input file by it's RVA.
fn get_slice_rva<'a, T>(data: &'a mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize, len: usize) -> &'a mut [T] {

    let file_offset = match rva_to_fo(data, sec_table_offs, num_secs, rva) {
        Some(fo) => fo,
        None => panic!("[-] Invalid input file: Pointer to an invalid RVA encountered.")
    };

    if mem::size_of::<T>() * len > data.len() - file_offset {
        panic!("[-] Invalid input file: file address exceeds file boundaries.");
    }

    unsafe { slice::from_raw_parts_mut(mem::transmute(
        data.as_ptr() as usize + file_offset), len) }
}

/// Translates an RVA to a file offset.
fn rva_to_fo(data: &mut Vec<u8>, sec_table_offs: usize,
    num_secs: usize, rva: usize) -> Option<usize> {

    for i in 0..num_secs {
        let cur_sec = get_data::<pe::ImageSectionHeader>(
            data, sec_table_offs + mem::size_of::<pe::ImageSectionHeader>() * i);

        unsafe {
            let sec_va = (*cur_sec).virtual_address as usize;
            if rva >= sec_va && rva <= sec_va + (*cur_sec).virtual_size as usize {
                return Some(rva - sec_va + (*cur_sec).pointer_to_raw_data as usize);
            }
        }
    }

    None
}<|endoftext|>


<commit_before>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::{ramp_interface};

type DB = RwLock<Database>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = RwLock::new(Database::new());
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let value = params.get_value().unwrap();

            let deps = {
                let target = params.get_dependencies().unwrap();
                let size = target.len();
                let mut entries = Vec::with_capacity(size as usize);
                for i in 0..size {
                    entries.push(target.get(i).unwrap().to_string());
                }
                entries
            };

            // let deps = params.get_dependencies().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap(); // hold lock till prepare is done
            db.prepare(key.to_string(), value.to_string(), deps, timestamp);
        }

        context.done();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {
        {
            let (params, mut results) = context.get();
            let ts = params.get_timestamp();
            let mut db = self.db.write().unwrap();
            db.commit(ts);

        }

        context.done();
    }

    fn get(&mut self, mut context: ramp_interface::GetContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();

            let mut db = self.db.write().unwrap();
            let version = db.get(key.to_string());

            let r = results.init_result();

        }
        context.done();
    }

    fn get_version(&mut self, mut context: ramp_interface::GetVersionContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap();
            let version = db.get_version(key.to_string(), timestamp);

            match version {
                Some(v) => {
                    let mut r = results.init_result();
                    let mut ver = r.init_version();
                    ver.set_value(&v.value);
                    ver.set_timestamp(v.timestamp);

                    let deps = ver.init_dependencies(v.dependencies.len() as u32);

                },
                None => {
                    let mut r = results.init_result();
                    r.set_none(());
                }
            };
        }

        context.done();
    }
}

fn main() {
    println!("Hello, world!");

    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();

    let ramp = Box::new(RampServer::new());
    let ramp_server = Box::new(ramp_interface::ServerDispatch { server : ramp });
    rpc_server.serve(ramp_server);

    println!("Goodbye forever.");
}
<commit_msg>setting each of the dependencies in get version<commit_after>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::{ramp_interface};

type DB = RwLock<Database>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = RwLock::new(Database::new());
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let value = params.get_value().unwrap();

            let deps = {
                let target = params.get_dependencies().unwrap();
                let size = target.len();
                let mut entries = Vec::with_capacity(size as usize);
                for i in 0..size {
                    entries.push(target.get(i).unwrap().to_string());
                }
                entries
            };

            // let deps = params.get_dependencies().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap(); // hold lock till prepare is done
            db.prepare(key.to_string(), value.to_string(), deps, timestamp);
        }

        context.done();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {
        {
            let (params, mut results) = context.get();
            let ts = params.get_timestamp();
            let mut db = self.db.write().unwrap();
            db.commit(ts);

        }

        context.done();
    }

    fn get(&mut self, mut context: ramp_interface::GetContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();

            let mut db = self.db.write().unwrap();
            let version = db.get(key.to_string());

            let r = results.init_result();

        }
        context.done();
    }

    fn get_version(&mut self, mut context: ramp_interface::GetVersionContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap();
            let version = db.get_version(key.to_string(), timestamp);

            match version {
                Some(v) => {
                    let mut r = results.init_result();
                    let mut ver = r.init_version();
                    ver.set_value(&v.value);
                    ver.set_timestamp(v.timestamp);

                    let len = v.dependencies.len() as u32;
                    let mut deps = ver.init_dependencies(len);
                    for i in 0..len {
                        deps.set(i as u32, &v.dependencies.get(i as usize).unwrap());
                    }
                },
                None => {
                    let mut r = results.init_result();
                    r.set_none(());
                }
            };
        }

        context.done();
    }
}

fn main() {
    println!("Hello, world!");

    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();

    let ramp = Box::new(RampServer::new());
    let ramp_server = Box::new(ramp_interface::ServerDispatch { server : ramp });
    rpc_server.serve(ramp_server);

    println!("Goodbye forever.");
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate time;
extern crate docopt;
extern crate itertools;
extern crate rustc_serialize;
extern crate unicode_segmentation;

mod display;
mod world;
mod parser;
mod util;

use std::thread::sleep_ms;
use docopt::Docopt;

use world::World;

static USAGE: &'static str = "
Usage:
    golr [options]

Options:
    --help                                      Show this message
    -h <height>, --height <height>              World height, points                 [default: 25]
    -w <width>, --width <width>                 World width, points                  [default: 80]
    -p <period>, --period <period>              World generational change period, ms [default: 350]
    -i <inputfile>, --inputfile <inputfile>     Populate world from file
";

#[derive(RustcDecodable, Debug)]
struct CliArgs {
    flag_width: isize,
    flag_height: isize,
    flag_period: u64,
    flag_inputfile: String,
}

fn main() {

    let args = Docopt::new(USAGE)
        .and_then(|d| d.decode::<CliArgs>())
        .unwrap_or_else(|e| e.exit());

    let seed = if args.flag_inputfile.is_empty() {
        None
    } else {
        util::read_file_to_string(&args.flag_inputfile)
            .and_then(|d: String| Ok(Some(parser::parse_plaintext(d))))
            .unwrap_or(None)
    };

    let mut world = World::new(args.flag_width, args.flag_height).seed(seed);

    print!("\x1b[2J");

    loop {
        let t_start = util::time_ms();
        println!("\x1b[H{}", world.evolve());
        let t_taken = util::time_ms() - t_start;

        if t_taken < args.flag_period {
            std::thread::sleep_ms((args.flag_period - t_taken) as u32);
        };

    }
}
<commit_msg>render flag<commit_after>extern crate rand;
extern crate time;
extern crate docopt;
extern crate itertools;
extern crate rustc_serialize;
extern crate unicode_segmentation;

mod display;
mod world;
mod parser;
mod util;

use std::thread::sleep_ms;
use docopt::Docopt;

use world::World;

static USAGE: &'static str = "
Usage:
    golr [options]

Options:
    --help                                      Show this message
    -h <height>, --height <height>              World height, points                         [default: 25]
    -w <width>, --width <width>                 World width, points                          [default: 80]
    -p <period>, --period <period>              World generational change period, ms         [default: 350]
    -i <inputfile>, --inputfile <inputfile>     Populate world from file
    -r <render>, --render <render>              Choose render engine [0-console | 1-piston]  [default: 0]
";

#[derive(RustcDecodable, Debug)]
struct CliArgs {
    flag_width: isize,
    flag_height: isize,
    flag_period: u64,
    flag_inputfile: String,
    flag_render: isize,
}

fn start_console_loop(world: &mut World, period: u64) {
    print!("\x1b[2J");
    loop {
        let t_start = util::time_ms();
        println!("\x1b[H{}", world.evolve());
        let t_taken = util::time_ms() - t_start;

        if t_taken < period {
            std::thread::sleep_ms((period - t_taken) as u32);
        };
    }
}

fn main() {

    let args = Docopt::new(USAGE)
        .and_then(|d| d.decode::<CliArgs>())
        .unwrap_or_else(|e| e.exit());

    let seed = if args.flag_inputfile.is_empty() {
        None
    } else {
        util::read_file_to_string(&args.flag_inputfile)
            .and_then(|d: String| Ok(Some(parser::parse_plaintext(d))))
            .unwrap_or(None)
    };

    let mut world = World::new(args.flag_width, args.flag_height).seed(seed);

    match args.flag_render {
        0 => start_console_loop(&mut world, args.flag_period),
        _ => panic!("Unknown render engine")
    };
}
<|endoftext|>


<commit_before>trait StringChars<T> {
    fn all_chars(self) -> Vec<T>;
}

impl StringChars<String> for String {
    fn all_chars(self) -> Vec<String> {
        let char_vec: Vec<String> = self
                                    .split("")
                                    .map(|cur_char| { cur_char.to_string() })
                                    .collect();
        char_vec
    }
}

trait VecExtensions<T> {
    fn last_pos(self) -> usize;
}

impl VecExtensions<String> for Vec<String> {
    fn last_pos(self) -> usize {
        self.len() - 1
    }
}

fn vec_shift(input: &Vec<String>, start: usize) -> String {
    input
    .iter()
    .enumerate()
    .filter(|&(i, _)| { i >= start })
    .map(|(i, x)| { x.to_string() })
    .collect::<Vec<String>>()
    .join("")
}

fn vec_shift_with_end(input: &Vec<String>, start: usize, end: usize) -> String {
    input
    .iter()
    .enumerate()
    .filter(|&(i, _)| { i >= start && i <= end })
    .map(|(i, x)| { x.to_string() })
    .collect::<Vec<String>>()
    .join("")
}

fn shift_seq(offset: i32, base_seq: String) -> String {
    let mut new_seq = String::new();
    let seq_vec: Vec<String> = base_seq.all_chars();

    if offset >= seq_vec.len() as i32 {
        new_seq = vec_shift(&seq_vec, (offset as usize) % seq_vec.len());
        if offset % seq_vec.len() as i32 != 0 {
            new_seq = format!("{}{}", new_seq, vec_shift(&seq_vec, 0))
        }
    } else {
        new_seq = vec_shift(&seq_vec, offset as usize);
        if offset != 0 {
            new_seq = format!("{}{}", new_seq, vec_shift_with_end(&seq_vec, 0, (offset - 1) as usize))
        }
    }
    new_seq
}

fn main() {
    // PROBLEMS:
    // - Doesn't add Everything before starting point to the end (PROBLEM part of 3)
    //   With offset over 26 it works again
    // - Doesn't start shifting at 1, but at 2
    // - In Else part, fix the offset != 0 problem

    let shift = shift_seq(3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string());
    println!("{}", shift);
}
<commit_msg>Fix shift at higher numbers<commit_after>trait StringChars<T> {
    fn all_chars(self) -> Vec<T>;
}

impl StringChars<String> for String {
    fn all_chars(self) -> Vec<String> {
        let char_vec: Vec<String> = self
                                    .split("")
                                    .map(|cur_char| { cur_char.to_string() })
                                    .collect();
        char_vec
    }
}

trait VecExtensions<T> {
    fn last_pos(&self) -> usize;
}

impl VecExtensions<String> for Vec<String> {
    fn last_pos(&self) -> usize {
        self.len() - 1
    }
}

fn vec_shift(input: &Vec<String>, start: usize, end: usize) -> String {
    input
    .iter()
    .enumerate()
    .filter(|&(i, _)| { i >= start && i <= end }) // end needed for one case otherwise not
    .map(|(i, x)| { x.to_string() })
    .collect::<Vec<String>>()
    .join("")
}

fn shift_seq(offset: i32, base_seq: String) -> String {
    let mut new_seq = String::new();
    let seq_vec: Vec<String> = base_seq.all_chars();

    if offset >= seq_vec.len() as i32 {
        println!("Here's a problem!");
        new_seq = vec_shift(&seq_vec, (offset as usize) % seq_vec.len(), seq_vec.last_pos());
        if offset % seq_vec.len() as i32 != 0 {
            new_seq = format!("{}{}", new_seq, vec_shift(&seq_vec, 0, (offset as usize) % seq_vec.len() - 1))
        }
    } else {
        new_seq = vec_shift(&seq_vec, offset as usize, seq_vec.last_pos());
        if offset != 0 {
            new_seq = format!("{}{}", new_seq, vec_shift(&seq_vec, 0, (offset - 1) as usize))
        }
    }
    new_seq
}

fn main() {
    // PROBLEMS:
    // - Doesn't add Everything before starting point to the end (PROBLEM part of 3)
    //   With offset over 26 it works again
    // - Doesn't start shifting at 1, but at 2
    // - In Else part, fix the offset != 0 problem

    let shift = shift_seq(30, "ABCDEFGHIJKLMNOPQRSTUVWXYZ".to_string());
    println!("{}", shift);
}
<|endoftext|>


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

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(u32),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.source.chars().nth(self.current - 1).unwrap()
    }

    fn add_simple_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.source.chars().skip(self.start).take(self.current - self.start).collect(),
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        match self.advance() {
            '(' => Ok(self.add_simple_context(Token::LeftParen)),
            ')' => Ok(self.add_simple_context(Token::RightParen)),
            '{' => Ok(self.add_simple_context(Token::LeftBrace)),
            '}' => Ok(self.add_simple_context(Token::RightBrace)),
            ',' => Ok(self.add_simple_context(Token::Comma)),
            '.' => Ok(self.add_simple_context(Token::Dot)),
            '-' => Ok(self.add_simple_context(Token::Minus)),
            '+' => Ok(self.add_simple_context(Token::Plus)),
            ';' => Ok(self.add_simple_context(Token::Semicolon)),
            '*' => Ok(self.add_simple_context(Token::Star)),
            _ => Err(format!("Unexpected character at line {}, pos {}", self.line, self.current))
        }
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        match scanner.scan_next(){
            Ok(token) => tokens.push(token),
            Err(message) => return Err(message)
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<commit_msg>Operators<commit_after>use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(u32),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if (self.is_at_end()) {
            return false;
        }
        if (self.char_at(self.current) != expected) {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.source.chars().skip(self.start).take(self.current - self.start).collect(),
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            _ => {
                return Err(format!("Unexpected character at line {}, pos {}",
                                   self.line,
                                   self.current))
            }
        };
        Ok(self.add_context(token))
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        match scanner.scan_next() {
            Ok(token) => tokens.push(token),
            Err(message) => return Err(message),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<|endoftext|>


<commit_before>
mod curses;

use std::ffi::CString;

use curses::*;

// TODO delete active rectangle
// TODO save to file (just save shape coords and types, etc)
// TODO load file

// TODO use struct with common fields and use an enum for unshared ones
enum DrawMe {
    Rec { width : i32, height : i32, x : i32, y : i32 },
    /*
    Character
    */
}

enum Mode { Normal, EnterRec, MoveRec }

fn inc_width( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width + 1, height: height, x: x, y: y },
        _ => panic!( "inc width encounters non-rec" ),
    }
}

fn inc_height( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height + 1, x: x, y: y },
        _ => panic!( "inc height encounters non-rec" ),
    }
}

fn dec_width( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width - 1, height: height, x: x, y: y },
        _ => panic!( "dec width encounters non-rec" ),
    }
}

fn dec_height( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height - 1, x: x, y: y },
        _ => panic!( "dec height encounters non-rec" ),
    }
}

fn inc_x( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x + 1, y: y },
        _ => panic!( "inc x encounters non-rec" ),
    }
}

fn inc_y( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x, y: y + 1 },
        _ => panic!( "inc y encounters non-rec" ),
    }
}

fn dec_x( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x - 1, y: y },
        _ => panic!( "dec x encounters non-rec" ),
    }
}

fn dec_y( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x, y: y - 1 },
        _ => panic!( "dec y encounters non-rec" ),
    }
}
fn mod_rec<F>( maybe_d : Option<DrawMe>, f : F ) -> Option<DrawMe>
    where F : Fn( DrawMe ) -> DrawMe {

    match maybe_d {
        Some( d ) => Some( f( d ) ),
        None => { None },
    }
}

unsafe fn render_shape( s : &DrawMe ) {
    match s {
        &DrawMe::Rec { width, height, x, y } => render_rec( width, height, x, y ),
        _ => (),
    }
}

unsafe fn render_rec( w : i32, h : i32, x : i32, y : i32 ) {
    let dot = CString::new( "." ).unwrap();
    let h_line = CString::new( "-" ).unwrap();
    let v_line = CString::new( "|" ).unwrap();
    let plus = CString::new( "+" ).unwrap();

    let mut h2 = h + 2;
    while h2 > 0 {
        let d : &CString = if h2 == h + 2 {
            &h_line
        } else if h2 == 1 {
            &h_line
        } else {
            &dot
        };
        let mut w2 = w;

        let other_d : &CString = if h2 == h + 2 {
            &plus
        } else if h2 == 1 {
            &plus
        } else {
            &v_line
        };
        mvprintw( y + h2 + UserY, x + w2 + 1 + UserX, other_d.as_ptr() );
        while w2 > 0 {
            mvprintw( y + h2 + UserY, x + w2 + UserX, d.as_ptr() );
            w2 -= 1;
        }
        mvprintw( y + h2 + UserY, x + UserX, other_d.as_ptr() );
        h2 -= 1;
    }
}

static mut UserX : i32 = 0;
static mut UserY : i32 = 0;

fn main() {
    let mut x = 0;
    let mut y = 0;
    unsafe {
        initscr();
        cbreak();
        noecho();

        let mut c = getch();
        let mut shapes : Vec<DrawMe> = vec! [];
        let mut mode : Mode = Mode::Normal;
        let mut active_rec : Option<DrawMe> = None;

        while c as u8 as char != 'q' {
            let c2 = c as u8 as char;

            match c2 {
                'r' => {
                    match mode {
                        Mode::Normal => {
                            active_rec = Some( DrawMe::Rec { width: 0, height: 0, x: x - UserX, y: y - UserY });
                            mode = Mode::EnterRec;
                        },
                        Mode::EnterRec => {
                            mode = Mode::MoveRec;
                        },
                        Mode::MoveRec => {
                            mode = Mode::Normal;
                            shapes.push( active_rec.unwrap() );
                            active_rec = None;
                        },
                    }
                },
                'j' => {
                    match mode {
                        Mode::Normal => {
                            y+=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, inc_height );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, inc_y );
                        },
                    }
                },
                'k' => {
                    match mode {
                       Mode::Normal => {
                           y-=1;
                       },
                       Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, dec_height );
                       },
                       Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, dec_y );
                       },
                    }
                },
                'l' => {
                    match mode {
                        Mode::Normal => {
                            x+=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, inc_width );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, inc_x );
                        },
                    }
                },
                'h' => {
                    match mode {
                        Mode::Normal => {
                            x-=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, dec_width );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, dec_x );
                        },
                    }
                },
                'J' => {
                    match mode {
                        Mode::Normal => {
                            UserY += 1;
                        },
                        _ => {},
                    }
                },
                'K' => {
                    match mode {
                        Mode::Normal => {
                            UserY -= 1;
                        },
                        _ => {},
                    }
                },
                'L' => {
                    match mode {
                        Mode::Normal => {
                            UserX += 1;
                        },
                        _ => {},
                    }
                },
                'H' => {
                    match mode {
                        Mode::Normal => {
                            UserX -= 1;
                        },
                        _ => {},
                    }
                },
                _ => (),
            }

            clear();

            match active_rec {
                Some( ref shape ) => render_shape( &shape ),
                None => {},
            }

            for s in &shapes {
                render_shape( s );
            }

            cursor_move( y, x );
            refresh();

            c = getch();
        }

        endwin();
    }
}

<commit_msg>character impl<commit_after>
mod curses;

use std::ffi::CString;

use curses::*;

// TODO delete active rectangle
// TODO save to file (just save shape coords and types, etc)
// TODO load file

// TODO use struct with common fields and use an enum for unshared ones
enum DrawMe {
    Rec { width : i32, height : i32, x : i32, y : i32 },
    Character { c : char, x : i32, y : i32 },
}

enum Mode { Normal, EnterRec, MoveRec, EnterCharacter }

fn inc_width( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width + 1, height: height, x: x, y: y },
        _ => panic!( "inc width encounters non-rec" ),
    }
}

fn inc_height( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height + 1, x: x, y: y },
        _ => panic!( "inc height encounters non-rec" ),
    }
}

fn dec_width( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width - 1, height: height, x: x, y: y },
        _ => panic!( "dec width encounters non-rec" ),
    }
}

fn dec_height( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height - 1, x: x, y: y },
        _ => panic!( "dec height encounters non-rec" ),
    }
}

fn inc_x( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x + 1, y: y },
        _ => panic!( "inc x encounters non-rec" ),
    }
}

fn inc_y( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x, y: y + 1 },
        _ => panic!( "inc y encounters non-rec" ),
    }
}

fn dec_x( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x - 1, y: y },
        _ => panic!( "dec x encounters non-rec" ),
    }
}

fn dec_y( d : DrawMe ) -> DrawMe {
    match d {
        DrawMe::Rec { width, height, x, y } => DrawMe::Rec { width: width, height: height, x: x, y: y - 1 },
        _ => panic!( "dec y encounters non-rec" ),
    }
}
fn mod_rec<F>( maybe_d : Option<DrawMe>, f : F ) -> Option<DrawMe>
    where F : Fn( DrawMe ) -> DrawMe {

    match maybe_d {
        Some( d ) => Some( f( d ) ),
        None => { None },
    }
}

unsafe fn render_shape( s : &DrawMe ) {
    match s {
        &DrawMe::Rec { width, height, x, y } => render_rec( width, height, x, y ),
        &DrawMe::Character { c, x, y } => render_character( c, x, y ),
        _ => (),
    }
}

unsafe fn render_character( c : char, x : i32, y : i32 ) {
    mvaddch( y + UserY, x + UserX, c );
}

unsafe fn render_rec( w : i32, h : i32, x : i32, y : i32 ) {
    let dot = CString::new( "." ).unwrap();
    let h_line = CString::new( "-" ).unwrap();
    let v_line = CString::new( "|" ).unwrap();
    let plus = CString::new( "+" ).unwrap();

    let mut h2 = h + 2;
    while h2 > 0 {
        let d : &CString = if h2 == h + 2 {
            &h_line
        } else if h2 == 1 {
            &h_line
        } else {
            &dot
        };
        let mut w2 = w;

        let other_d : &CString = if h2 == h + 2 {
            &plus
        } else if h2 == 1 {
            &plus
        } else {
            &v_line
        };
        mvprintw( y + h2 + UserY, x + w2 + 1 + UserX, other_d.as_ptr() );
        while w2 > 0 {
            mvprintw( y + h2 + UserY, x + w2 + UserX, d.as_ptr() );
            w2 -= 1;
        }
        mvprintw( y + h2 + UserY, x + UserX, other_d.as_ptr() );
        h2 -= 1;
    }
}

static mut UserX : i32 = 0;
static mut UserY : i32 = 0;

fn main() {
    let mut x = 0;
    let mut y = 0;
    unsafe {
        initscr();
        cbreak();
        noecho();

        let mut c = getch();
        let mut shapes : Vec<DrawMe> = vec! [];
        let mut mode : Mode = Mode::Normal;
        let mut active_rec : Option<DrawMe> = None;

        while c as u8 as char != 'q' {
            let c2 = c as u8 as char;

            match c2 {
                'c' => {
                    match mode {
                        Mode::Normal => {
                            mode = Mode::EnterCharacter;
                        },
                        Mode::EnterRec => {},
                        Mode::MoveRec => {},
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'c', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                    }
                },
                'r' => {
                    match mode {
                        Mode::Normal => {
                            active_rec = Some( DrawMe::Rec { width: 0, height: 0, x: x - UserX, y: y - UserY });
                            mode = Mode::EnterRec;
                        },
                        Mode::EnterRec => {
                            mode = Mode::MoveRec;
                        },
                        Mode::MoveRec => {
                            mode = Mode::Normal;
                            shapes.push( active_rec.unwrap() );
                            active_rec = None;
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'r', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                    }
                },
                'j' => {
                    match mode {
                        Mode::Normal => {
                            y+=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, inc_height );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, inc_y );
                        },

                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'j', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                    }
                },
                'k' => {
                    match mode {
                       Mode::Normal => {
                           y-=1;
                       },
                       Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, dec_height );
                       },
                       Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, dec_y );
                       },

                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'k', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                    }
                },
                'l' => {
                    match mode {
                        Mode::Normal => {
                            x+=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, inc_width );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, inc_x );
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'l', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },

                    }
                },
                'h' => {
                    match mode {
                        Mode::Normal => {
                            x-=1;
                        },
                        Mode::EnterRec => {
                            active_rec = mod_rec( active_rec, dec_width );
                        },
                        Mode::MoveRec => {
                            active_rec = mod_rec( active_rec, dec_x );
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'h', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                    }
                },
                'J' => {
                    match mode {
                        Mode::Normal => {
                            UserY += 1;
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'J', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                        _ => {},
                    }
                },
                'K' => {
                    match mode {
                        Mode::Normal => {
                            UserY -= 1;
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'K', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                        _ => {},
                    }
                },
                'L' => {
                    match mode {
                        Mode::Normal => {
                            UserX += 1;
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'L', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                        _ => {},
                    }
                },
                'H' => {
                    match mode {
                        Mode::Normal => {
                            UserX -= 1;
                        },
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: 'H', x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                        _ => {},
                    }
                },
                c3 @ _ => {
                    match mode {
                        Mode::EnterCharacter => {
                            shapes.push( DrawMe::Character{ c: c3, x: x - UserX, y: y - UserY } );
                            mode = Mode::Normal;
                        },
                        _ => {},
                    }
                },

            }

            clear();

            match active_rec {
                Some( ref shape ) => render_shape( &shape ),
                None => {},
            }

            for s in &shapes {
                render_shape( s );
            }

            cursor_move( y, x );
            refresh();

            c = getch();
        }

        endwin();
    }
}

<|endoftext|>