<commit_before>// Copyright 2018 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Different assorted utilities.

pub use self::types::{Height, Milliseconds, Round, ValidatorId};

pub mod config;
pub mod fabric;
pub mod user_agent;
#[macro_use]
pub mod metrics;

use chrono::{DateTime, Local};
use colored::*;
use env_logger::{Builder, Formatter};
use log::{Level, Record, SetLoggerError};

use std::{
    env, io::{self, Write}, time::SystemTime,
};

use blockchain::{GenesisConfig, ValidatorKeys};
use crypto::gen_keypair;
use node::{ConnectListConfig, NodeConfig};

mod types;

/// Performs the logger initialization.
pub fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = Builder::new();
    builder.format(format_log_record);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.try_init()
}

/// Generates testnet configuration.
pub fn generate_testnet_config(count: u8, start_port: u16) -> Vec<NodeConfig> {
    let (validators, services): (Vec<_>, Vec<_>) = (0..count as usize)
        .map(|_| (gen_keypair(), gen_keypair()))
        .unzip();
    let genesis = GenesisConfig::new(validators.iter().zip(services.iter()).map(|x| {
        ValidatorKeys {
            consensus_key: (x.0).0,
            service_key: (x.1).0,
        }
    }));
    let peers = (0..validators.len())
        .map(|x| {
            format!("127.0.0.1:{}", start_port + x as u16)
                .parse()
                .unwrap()
        })
        .collect::<Vec<_>>();

    validators
        .into_iter()
        .zip(services.into_iter())
        .enumerate()
        .map(|(idx, (validator, service))| NodeConfig {
            listen_address: peers[idx],
            external_address: Some(peers[idx]),
            network: Default::default(),
            consensus_public_key: validator.0,
            consensus_secret_key: validator.1,
            service_public_key: service.0,
            service_secret_key: service.1,
            genesis: genesis.clone(),
            connect_list: ConnectListConfig::from_validator_keys(&genesis.validator_keys, &peers),
            api: Default::default(),
            mempool: Default::default(),
            services_configs: Default::default(),
            database: Default::default(),
        })
        .collect::<Vec<_>>()
}

fn has_colors() -> bool {
    use atty;
    use std::io;
    use term::terminfo::TerminfoTerminal;
    use term::Terminal;

    let out = io::stderr();
    match TerminfoTerminal::new(out) {
        Some(ref term) if atty::is(atty::Stream::Stderr) => term.supports_color(),
        _ => false,
    }
}

fn format_time(time: SystemTime) -> String {
    DateTime::<Local>::from(time).to_rfc2822()
}

fn format_log_record(buf: &mut Formatter, record: &Record) -> io::Result<()> {
    let time = format_time(SystemTime::now());

    let verbose_src_path = match env::var("RUST_VERBOSE_PATH") {
        Ok(val) => val.parse::<bool>().unwrap_or(false),
        Err(_) => false,
    };

    let module = record.module_path().unwrap_or("unknown_module");
    let source_path = if verbose_src_path {
        let file = record.file().unwrap_or("unknown_file");
        let line = record.line().unwrap_or(0);
        format!("{}:{}:{}", module, file, line)
    } else {
        module.to_string()
    };

    if has_colors() {
        let level = match record.level() {
            Level::Error => "ERROR".red(),
            Level::Warn => "WARN".yellow(),
            Level::Info => "INFO".green(),
            Level::Debug => "DEBUG".cyan(),
            Level::Trace => "TRACE".white(),
        };
        writeln!(
            buf,
            "{} {} {} {}",
            time.dimmed(),
            level,
            source_path.dimmed(),
            record.args()
        )
    } else {
        let level = match record.level() {
            Level::Error => "ERROR",
            Level::Warn => "WARN",
            Level::Info => "INFO",
            Level::Debug => "DEBUG",
            Level::Trace => "TRACE",
        };
        writeln!(buf, "{} {} {} {}", time, level, &source_path, record.args())
    }
}
<commit_msg>Include milliseconds in log timestamps (#688) (#796)<commit_after>// Copyright 2018 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Different assorted utilities.

pub use self::types::{Height, Milliseconds, Round, ValidatorId};

pub mod config;
pub mod fabric;
pub mod user_agent;
#[macro_use]
pub mod metrics;

use chrono::{DateTime, Utc};
use colored::*;
use env_logger::{Builder, Formatter};
use log::{Level, Record, SetLoggerError};

use std::{
    env, io::{self, Write}, time::SystemTime,
};

use blockchain::{GenesisConfig, ValidatorKeys};
use crypto::gen_keypair;
use node::{ConnectListConfig, NodeConfig};

mod types;

/// Format for timestamps in logs.
///
/// It is similar to date/time format of RFC 2822, but with milliseconds:
/// "Mon, 16 Jul 2018 13:37:18.594 +0100"
const LOG_TIMESTAMP_FORMAT: &str = "%a, %e %b %Y %H:%M:%S%.3f %z";

/// Performs the logger initialization.
pub fn init_logger() -> Result<(), SetLoggerError> {
    let mut builder = Builder::new();
    builder.format(format_log_record);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.try_init()
}

/// Generates testnet configuration.
pub fn generate_testnet_config(count: u8, start_port: u16) -> Vec<NodeConfig> {
    let (validators, services): (Vec<_>, Vec<_>) = (0..count as usize)
        .map(|_| (gen_keypair(), gen_keypair()))
        .unzip();
    let genesis = GenesisConfig::new(validators.iter().zip(services.iter()).map(|x| {
        ValidatorKeys {
            consensus_key: (x.0).0,
            service_key: (x.1).0,
        }
    }));
    let peers = (0..validators.len())
        .map(|x| {
            format!("127.0.0.1:{}", start_port + x as u16)
                .parse()
                .unwrap()
        })
        .collect::<Vec<_>>();

    validators
        .into_iter()
        .zip(services.into_iter())
        .enumerate()
        .map(|(idx, (validator, service))| NodeConfig {
            listen_address: peers[idx],
            external_address: Some(peers[idx]),
            network: Default::default(),
            consensus_public_key: validator.0,
            consensus_secret_key: validator.1,
            service_public_key: service.0,
            service_secret_key: service.1,
            genesis: genesis.clone(),
            connect_list: ConnectListConfig::from_validator_keys(&genesis.validator_keys, &peers),
            api: Default::default(),
            mempool: Default::default(),
            services_configs: Default::default(),
            database: Default::default(),
        })
        .collect::<Vec<_>>()
}

fn has_colors() -> bool {
    use atty;
    use std::io;
    use term::terminfo::TerminfoTerminal;
    use term::Terminal;

    let out = io::stderr();
    match TerminfoTerminal::new(out) {
        Some(ref term) if atty::is(atty::Stream::Stderr) => term.supports_color(),
        _ => false,
    }
}

fn format_time(time: SystemTime) -> String {
    DateTime::<Utc>::from(time)
        .format(LOG_TIMESTAMP_FORMAT)
        .to_string()
}

fn format_log_record(buf: &mut Formatter, record: &Record) -> io::Result<()> {
    let time = format_time(SystemTime::now());

    let verbose_src_path = match env::var("RUST_VERBOSE_PATH") {
        Ok(val) => val.parse::<bool>().unwrap_or(false),
        Err(_) => false,
    };

    let module = record.module_path().unwrap_or("unknown_module");
    let source_path = if verbose_src_path {
        let file = record.file().unwrap_or("unknown_file");
        let line = record.line().unwrap_or(0);
        format!("{}:{}:{}", module, file, line)
    } else {
        module.to_string()
    };

    if has_colors() {
        let level = match record.level() {
            Level::Error => "ERROR".red(),
            Level::Warn => "WARN".yellow(),
            Level::Info => "INFO".green(),
            Level::Debug => "DEBUG".cyan(),
            Level::Trace => "TRACE".white(),
        };
        writeln!(
            buf,
            "{} {} {} {}",
            time.dimmed(),
            level,
            source_path.dimmed(),
            record.args()
        )
    } else {
        let level = match record.level() {
            Level::Error => "ERROR",
            Level::Warn => "WARN",
            Level::Info => "INFO",
            Level::Debug => "DEBUG",
            Level::Trace => "TRACE",
        };
        writeln!(buf, "{} {} {} {}", time, level, &source_path, record.args())
    }
}

#[cfg(test)]
mod tests {
    use std::time;

    use super::*;

    #[test]
    fn time_formatting() {
        assert_eq!(
            format_time(time::UNIX_EPOCH),
            "Thu,  1 Jan 1970 00:00:00.000 +0000"
        );
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

extern crate rand;
extern crate rayon;
extern crate time;

use rand::{Rng, SeedableRng, XorShiftRng};
use std::env;
use std::str::FromStr;
use rayon::InitResult::{NumberOfThreadsZero, NumberOfThreadsNotEqual};

trait Joiner {
    fn is_parallel() -> bool;
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send;
}

struct Parallel;
impl Joiner for Parallel {
    #[inline]
    fn is_parallel() -> bool {
        true
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        rayon::join(oper_a, oper_b)
    }
}

struct Sequential;
impl Joiner for Sequential {
    #[inline]
    fn is_parallel() -> bool {
        false
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        let a = oper_a();
        let b = oper_b();
        (a, b)
    }
}

fn quick_sort<J:Joiner, T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    if J::is_parallel() && v.len() <= 5*1024 {
        return quick_sort::<Sequential, T>(v);
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    J::join(|| quick_sort::<J,T>(lo),
            || quick_sort::<J,T>(hi));
}

fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
    let pivot = v.len() - 1;
    let mut i = 0;
    for j in 0..pivot {
        if v[j] <= v[pivot] {
            v.swap(i, j);
            i += 1;
        }
    }
    v.swap(i, pivot);
    i
}

fn main() {
    let mut kilo = 24;
    let mut run_par = true;
    let mut run_seq = true;
    let mut reps = 10;

    let mut args = env::args().skip(1);
    while let Some(arg) = args.next() {
        if arg == "--no-par" {
            run_par = false;
        } else if arg == "--no-seq" {
            run_seq = false;
        } else if arg == "--reps" {
            if let Some(reps_arg) = args.next() {
                match usize::from_str(&reps_arg) {
                    Ok(v) => reps = v,
                    Err(_) => {
                        println!("invalid argument `{}`, expected integer", reps_arg);
                    }
                }
            } else {
                println!("missing argument to --reps");
            }
        } else {
            match usize::from_str(&arg) {
                Ok(v) => kilo = v,
                Err(_) => {
                    println!("invalid argument `{}`, expected integer", arg);
                }
            }
        }
    }

    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
    let mut par_time = 0;
    let mut seq_time = 0;

    let result = rayon::Configuration::new().initialize();

    match result {
        Ok(_) => println!("Rayon initialization successfull"),
        Err(NumberOfThreadsZero) => println!("the number of threads must be greater than zero"),
        Err(NumberOfThreadsNotEqual) => println!("initialisation called multiple times with different number of threads")
    }

    for _ in 0 .. reps {
        let data: Vec<_> = (0..kilo*1024).map(|_| rng.next_u32()).collect();

        if run_par {
            let mut data = data.clone();
            let par_start = time::precise_time_ns();
            quick_sort::<Parallel, u32>(&mut data);
            par_time += time::precise_time_ns() - par_start;
        }

        if run_seq {
            let mut data = data.clone();
            let seq_start = time::precise_time_ns();
            quick_sort::<Sequential, u32>(&mut data);
            seq_time += time::precise_time_ns() - seq_start;
        }
    }

    println!("Array length    : {}K 32-bit integers", kilo);
    println!("Repetitions     : {}", reps);
    if run_par { println!("Parallel time   : {}", par_time); }
    if run_seq { println!("Sequential time : {}", seq_time); }
    if run_par && run_seq {
        println!("Parallel speedup: {}", (seq_time as f64) / (par_time as f64));
    }
}
<commit_msg>Assert that thread-pool is initialized.<commit_after>#![allow(non_camel_case_types)]

extern crate rand;
extern crate rayon;
extern crate time;

use rand::{Rng, SeedableRng, XorShiftRng};
use std::env;
use std::str::FromStr;

trait Joiner {
    fn is_parallel() -> bool;
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send;
}

struct Parallel;
impl Joiner for Parallel {
    #[inline]
    fn is_parallel() -> bool {
        true
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        rayon::join(oper_a, oper_b)
    }
}

struct Sequential;
impl Joiner for Sequential {
    #[inline]
    fn is_parallel() -> bool {
        false
    }
    #[inline]
    fn join<A,R_A,B,R_B>(oper_a: A, oper_b: B) -> (R_A, R_B)
        where A: FnOnce() -> R_A + Send, B: FnOnce() -> R_B + Send,
              R_A: Send, R_B: Send
    {
        let a = oper_a();
        let b = oper_b();
        (a, b)
    }
}

fn quick_sort<J:Joiner, T:PartialOrd+Send>(v: &mut [T]) {
    if v.len() <= 1 {
        return;
    }

    if J::is_parallel() && v.len() <= 5*1024 {
        return quick_sort::<Sequential, T>(v);
    }

    let mid = partition(v);
    let (lo, hi) = v.split_at_mut(mid);
    J::join(|| quick_sort::<J,T>(lo),
            || quick_sort::<J,T>(hi));
}

fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
    let pivot = v.len() - 1;
    let mut i = 0;
    for j in 0..pivot {
        if v[j] <= v[pivot] {
            v.swap(i, j);
            i += 1;
        }
    }
    v.swap(i, pivot);
    i
}

fn main() {
    let mut kilo = 24;
    let mut run_par = true;
    let mut run_seq = true;
    let mut reps = 10;

    let mut args = env::args().skip(1);
    while let Some(arg) = args.next() {
        if arg == "--no-par" {
            run_par = false;
        } else if arg == "--no-seq" {
            run_seq = false;
        } else if arg == "--reps" {
            if let Some(reps_arg) = args.next() {
                match usize::from_str(&reps_arg) {
                    Ok(v) => reps = v,
                    Err(_) => {
                        println!("invalid argument `{}`, expected integer", reps_arg);
                    }
                }
            } else {
                println!("missing argument to --reps");
            }
        } else {
            match usize::from_str(&arg) {
                Ok(v) => kilo = v,
                Err(_) => {
                    println!("invalid argument `{}`, expected integer", arg);
                }
            }
        }
    }

    let mut rng = XorShiftRng::from_seed([0, 1, 2, 3]);
    let mut par_time = 0;
    let mut seq_time = 0;

    // Just unwrap as nobody else ought to be initializing the
    // thread-pool by this time.
    rayon::initialize(rayon::Configuration::new()).unwrap();

    for _ in 0 .. reps {
        let data: Vec<_> = (0..kilo*1024).map(|_| rng.next_u32()).collect();

        if run_par {
            let mut data = data.clone();
            let par_start = time::precise_time_ns();
            quick_sort::<Parallel, u32>(&mut data);
            par_time += time::precise_time_ns() - par_start;
        }

        if run_seq {
            let mut data = data.clone();
            let seq_start = time::precise_time_ns();
            quick_sort::<Sequential, u32>(&mut data);
            seq_time += time::precise_time_ns() - seq_start;
        }
    }

    println!("Array length    : {}K 32-bit integers", kilo);
    println!("Repetitions     : {}", reps);
    if run_par { println!("Parallel time   : {}", par_time); }
    if run_seq { println!("Sequential time : {}", seq_time); }
    if run_par && run_seq {
        println!("Parallel speedup: {}", (seq_time as f64) / (par_time as f64));
    }
}
<|endoftext|>


<commit_before>use std::mem;

// This function borrows a slice
fn analyze_slice(slice: &[int]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Fixed-size array (type signature is superfluous)
    let xs: [int, ..5] = [1, 2, 3, 4, 5];

    // All elements can be initialized to the same value
    let ys: [int, ..500] = [0, ..500];

    // Indexing starts at 0
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // `len` returns the size of the array
    println!("array size: {}", xs.len());

    // Arrays are stack allocated
    println!("array occupies {} bytes", mem::size_of_val(&xs));

    // Arrays can be automatically borrowed as slices
    println!("borrow the whole array as a slice");
    analyze_slice(&xs);

    // Slices can point to a section of an array
    println!("borrow a section of the array as a slice");
    analyze_slice(ys.slice(1, 4));

    // Out of bound indexing yields a task failure
    println!("{}", xs[5]);
}
<commit_msg>array: Upgrade to new fixed-length array syntax<commit_after>use std::mem;

// This function borrows a slice
fn analyze_slice(slice: &[int]) {
    println!("first element of the slice: {}", slice[0]);
    println!("the slice has {} elements", slice.len());
}

fn main() {
    // Fixed-size array (type signature is superfluous)
    let xs: [int; 5] = [1, 2, 3, 4, 5];

    // All elements can be initialized to the same value
    let ys: [int; 500] = [0; 500];

    // Indexing starts at 0
    println!("first element of the array: {}", xs[0]);
    println!("second element of the array: {}", xs[1]);

    // `len` returns the size of the array
    println!("array size: {}", xs.len());

    // Arrays are stack allocated
    println!("array occupies {} bytes", mem::size_of_val(&xs));

    // Arrays can be automatically borrowed as slices
    println!("borrow the whole array as a slice");
    analyze_slice(&xs);

    // Slices can point to a section of an array
    println!("borrow a section of the array as a slice");
    analyze_slice(ys.slice(1, 4));

    // Out of bound indexing yields a task failure
    println!("{}", xs[5]);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate mqtt;

use std::thread;
use std::char;
use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions};

fn conf_logger() {
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            let t = time::now();
            let ms = t.tm_nsec/1000_000;
            format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg)
        }),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
        panic!("Failed to initialize global logger: {}", e);
    }
}

fn setup_mqtt(server_address: &str, topic: &str, client_id: &str) -> Result<AsyncClient, MqttError> {
    let connect_options = AsyncConnectOptions::new();
    let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing));
    try!(client.connect(&connect_options));
    try!(client.subscribe(topic, Qos::FireAndForget));
    Ok(client)
}

fn main() {
    // setup fern logger
    conf_logger();

    // start processing
    info!("sendreceive test started");
    let mut data = Vec::new();
    let topic = "TestTopic";
    match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId") {
        Ok(mut client) => {
            for i in 0..10 {
                info!("data len: {}", i);
                data.push(char::from_digit(i % 10, 10).unwrap() as u8);
                client.send(&data, &topic, Qos::FireAndForget).unwrap();
                for message in client.messages() {
                    info!("{:?}", message);
                }
                thread::sleep_ms(200);
            }},
        Err(e) => error!("{}", e)
    }
    info!("sendreceive test ended");
}
<commit_msg>demonstrate Error trait usage in sendreceive example<commit_after>#[macro_use]
extern crate log;
extern crate fern;
extern crate time;
extern crate mqtt;

use std::thread;
use std::char;
use mqtt::async::{PersistenceType, Qos, MqttError, AsyncClient, AsyncConnectOptions};
use std::error::Error;

fn conf_logger() {
    let logger_config = fern::DispatchConfig {
        format: Box::new(|msg: &str, level: &log::LogLevel, _location: &log::LogLocation| {
            let t = time::now();
            let ms = t.tm_nsec/1000_000;
            format!("{}.{:3} [{}] {}", t.strftime("%Y-%m-%dT%H:%M:%S").unwrap(), ms, level, msg)
        }),
        output: vec![fern::OutputConfig::stderr()],
        level: log::LogLevelFilter::Trace,
    };

    if let Err(e) = fern::init_global_logger(logger_config, log::LogLevelFilter::Trace) {
        panic!("Failed to initialize global logger: {}", e);
    }
}

fn setup_mqtt(server_address: &str, topic: &str, client_id: &str) -> Result<AsyncClient, MqttError> {
    let connect_options = AsyncConnectOptions::new();
    let mut client = try!(AsyncClient::new(server_address, client_id, PersistenceType::Nothing));
    try!(client.connect(&connect_options));
    try!(client.subscribe(topic, Qos::FireAndForget));
    Ok(client)
}

fn main() {
    // setup fern logger
    conf_logger();

    // start processing
    info!("sendreceive test started");
    let mut data = Vec::new();
    let topic = "TestTopic";
    match setup_mqtt("tcp://localhost:1883", &topic, "TestClientId") {
        Ok(mut client) => {
            for i in 0..10 {
                info!("data len: {}", i);
                data.push(char::from_digit(i % 10, 10).unwrap() as u8);
                client.send(&data, &topic, Qos::FireAndForget).unwrap();
                for message in client.messages() {
                    info!("{:?}", message);
                }
                thread::sleep_ms(200);
            }},
        Err(e) => error!("{}; raw error: {}", e.description(), e)
    }
    info!("sendreceive test ended");
}
<|endoftext|>


<commit_before>#![feature(std_misc)]
#![feature(core)]

extern crate para_bm;

use std::time::Duration;
use std::thread::Thread;
use std::num::Float;

use para_bm::{sleep_ns};

fn fast () {
    sleep_ns(1000);
}

fn slow () {
    sleep_ns(10000000);
}

fn my_bench<F> (f: F) -> f64 where F: Fn() {

    // first "throw away" run
    let mut d = Duration::span(|| {
        f();
    });
    let ns = d.num_nanoseconds().unwrap();
    //println!("num nano seconds first run {}", ns);

    let mut sum = 0;
    // iterate to get more cache hits, etc...
    for _ in 0 .. 10 {
        d = Duration::span(|| {
            f();
        });
        let ns = d.num_nanoseconds().unwrap();
        sum = sum + ns;
    }

    let avg = (sum as f64) / 10.0;
    //println!("average after first run {}", avg);

    return avg;
}

fn main () {

    let mut num_ns = 10.0;

    for power in 0 .. 20 {
        println!("Size.....: {}", num_ns as i64);

        let t_avg = my_bench(||{
            let threads_holder = (0 .. 3).map(|_| {
                Thread::scoped(move || {
                    sleep_ns(num_ns as i64);
                })
            }).collect::<Vec<_>>();
        });
        println!("Runtime thread....: {}", t_avg);

        let s_avg = my_bench(||{
            let serial_holder = (0 .. 3).map(|_| {
                sleep_ns(num_ns as i64);
            }).collect::<Vec<_>>();
        });
        println!("Runtime serial....: {}", s_avg);

        num_ns *= 2.0;
    }
}
<commit_msg>iterate back and forth over the space<commit_after>#![feature(std_misc)]
#![feature(core)]

extern crate para_bm;

use std::time::Duration;
use std::thread::Thread;
use std::num::Float;

use para_bm::{sleep_ns};

fn fast () {
    sleep_ns(1000);
}

fn slow () {
    sleep_ns(10000000);
}

fn my_bench<F> (f: F) -> f64 where F: Fn() {

    // first "throw away" run
    let mut d = Duration::span(|| {
        f();
    });
    let ns = d.num_nanoseconds().unwrap();
    //println!("num nano seconds first run {}", ns);

    let mut sum = 0;
    // iterate to get more cache hits, etc...
    let iters = 100.0;
    for _ in 0 .. iters as usize {
        d = Duration::span(|| {
            f();
        });
        let ns = d.num_nanoseconds().unwrap();
        sum = sum + ns;
    }

    let avg = (sum as f64) / iters;
    //println!("average after first run {}", avg);

    return avg;
}

fn main () {

    let mut max_ns = 10000000.0;
    let mut min_ns = 0.0;
    //let mut half_ns;
    let mut qurt_ns;
    let mut num_ns = (max_ns + min_ns) / 2.0;

    for power in 0 .. 30 {
        println!("Size.....: {}", num_ns as i64);

        let t_avg = my_bench(||{
            let threads_holder = (0 .. 3).map(|_| {
                Thread::scoped(move || {
                    sleep_ns(num_ns as i64);
                })
            }).collect::<Vec<_>>();
        });
        //println!("Runtime thread....: {}", t_avg);

        let s_avg = my_bench(||{
            let serial_holder = (0 .. 3).map(|_| {
                sleep_ns(num_ns as i64);
            }).collect::<Vec<_>>();
        });
        //println!("Runtime serial....: {}", s_avg);

        println!("s - t .....: {}", s_avg - t_avg);

        if t_avg > s_avg {
            qurt_ns = (max_ns + num_ns) / 2.0;
            min_ns = num_ns;
            num_ns = qurt_ns;
        } else {
            qurt_ns = (min_ns + num_ns) / 2.0;
            max_ns = num_ns;
            num_ns = qurt_ns;
        }

        println!("qurt.....: {}", qurt_ns as i64);
        println!("jump.....: {}", max_ns - min_ns);

        println!("------------------------------------");

        if max_ns - min_ns < 1.0 {

            break;
        }

    }
}
<|endoftext|>


<commit_before>//! Low level operations on vectors and matrices.

/// Trait for testing approximate equality
pub trait ApproxEq<Eps> {
    /// Default epsilon for approximation.
    fn approx_epsilon(unused_self: Option<Self>) -> Eps;

    /// Tests approximate equality using a custom epsilon.
    fn approx_eq_eps(a: &Self, other: &Self, epsilon: &Eps) -> bool;

    /// Tests approximate equality.
    fn approx_eq(a: &Self, b: &Self) -> bool {
        ApproxEq::approx_eq_eps(a, b, &ApproxEq::approx_epsilon(None::<Self>))
    }
}

impl ApproxEq<f32> for f32 {
    #[inline]
    fn approx_epsilon(_: Option<f32>) -> f32 {
        1.0e-6
    }

    #[inline]
    fn approx_eq_eps(a: &f32, b: &f32, epsilon: &f32) -> bool {
        (*a - *b).abs() < *epsilon
    }
}

impl ApproxEq<f64> for f64 {
    #[inline]
    fn approx_epsilon(_: Option<f64>) -> f64 {
        1.0e-6
    }

    #[inline]
    fn approx_eq_eps(a: &f64, b: &f64, approx_epsilon: &f64) -> bool {
        (*a - *b).abs() < *approx_epsilon
    }
}

/// Trait of objects having an absolute value.
/// This is useful if the object does not have the same type as its absolute value.
pub trait Absolute<A> {
    /// Computes some absolute value of this object.
    /// Typically, this will make all component of a matrix or vector positive.
    fn abs(&Self) -> A;
}

/// Trait of objects having an inverse. Typically used to implement matrix inverse.
pub trait Inv {
    /// Returns the inverse of `self`.
    fn inv_cpy(m: &Self) -> Option<Self>;

    /// In-place version of `inverse`.
    fn inv(&mut self) -> bool;
}

/// Trait of objects which can be transposed.
pub trait Transpose {
    /// Computes the transpose of a matrix.
    fn transpose_cpy(m: &Self) -> Self;

    /// In-place version of `transposed`.
    fn transpose(&mut self);
}

/// Traits of objects having an outer product.
pub trait Outer<M> {
    /// Computes the outer product: `self * other`
    fn outer(a: &Self, b: &Self) -> M;
}

/// Trait for computing the covariance of a set of data.
pub trait Cov<M> {
    /// Computes the covariance of the obsevations stored by `self`:
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn cov(&Self) -> M;

    /// Computes the covariance of the obsevations stored by `self`:
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn cov_to(m: &Self, out: &mut M) {
        *out = Cov::cov(m)
    }
}

/// Trait for computing the covariance of a set of data.
pub trait Mean<N> {
    /// Computes the mean of the observations stored by `self`.
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn mean(&Self) -> N;
}

// /// Cholesky decomposition.
// pub trait Chol {
//     /// Performs the cholesky decomposition on `self`. The resulting upper-triangular matrix is
//     /// returned. Returns `None` if the matrix is not symetric positive-definite.
//     fn chol(self) -> Option<Self>;
//
//     /// Performs the cholesky decomposition on `self`. The resulting upper-triangular matrix is
//     /// written to a given parameter. If the decomposition fails `false` is returned; the state of
//     /// the output parameter is undefined.
//     fn chol_to(&self, out: &mut Self) -> bool {
//         match self.chol() {
//             None => false,
//             Some(decomp) => {
//                 *out = decomp;
//                 true
//             }
//         }
//     }
// }

// XXX: those two traits should not exist since there is generalized operator overloading of Add
// and Sub.
// However, using the same trait multiple time as a trait bound (ex: impl<T: Add<N, V> + Add<V, V>)
// does not work properly, mainly because the way we are doing generalized operator overloading is
// verry hacky.
//
// Hopefully, this will be fixed on a future version of the language!
/// Trait of objects having a right multiplication with another element.
pub trait RMul<V> {
    /// Computes `self * v`
    fn rmul(&self, v: &V) -> V;
}

impl<M: Mul<T, T>, T> RMul<T> for M {
    fn rmul(&self, v: &T) -> T {
        self * *v
    }
}

/// Trait of objects having a left multiplication with another element.
pub trait LMul<V> {
    /// Computes `v * self`
    fn lmul(&self, &V) -> V;
}

impl<T: Mul<M, T>, M> LMul<T> for M {
    fn lmul(&self, v: &T) -> T {
        v * *self
    }
}

// XXX: those traits should not exist since there is generalized operator overloading of Add and
// Sub.
// However, using the same trait multiple time as a trait bound (ex: impl<T: Add<N, V> + Add<V, V>)
// does not work properly, mainly because the way we are doing generalized operator overloading is
// verry hacky.
//
// Hopefully, this will be fixed on a future version of the language!
/// Trait of objects having an addition with a scalar.
pub trait ScalarAdd<N> {
    /// Gets the result of `self + n`.
    fn add_s(&self, n: &N) -> Self;
}

impl<N, T: Add<N, T>> ScalarAdd<N> for T {
    /// Gets the result of `self + n`.
    fn add_s(&self, n: &N) -> T {
        *self + *n
    }
}

/**
 * Trait of objects having a subtraction with a scalar.
 */
pub trait ScalarSub<N> {
    /// Gets the result of `self - n`.
    fn sub_s(&self, n: &N) -> Self;
}

impl<N, T: Sub<N, T>> ScalarSub<N> for T {
    /// Gets the result of `self - n`.
    fn sub_s(&self, n: &N) -> T {
        *self - *n
    }
}
<commit_msg>Inline the default implementation of approx_eq<commit_after>//! Low level operations on vectors and matrices.

/// Trait for testing approximate equality
pub trait ApproxEq<Eps> {
    /// Default epsilon for approximation.
    fn approx_epsilon(unused_self: Option<Self>) -> Eps;

    /// Tests approximate equality using a custom epsilon.
    fn approx_eq_eps(a: &Self, other: &Self, epsilon: &Eps) -> bool;

    /// Tests approximate equality.
    #[inline]
    fn approx_eq(a: &Self, b: &Self) -> bool {
        ApproxEq::approx_eq_eps(a, b, &ApproxEq::approx_epsilon(None::<Self>))
    }
}

impl ApproxEq<f32> for f32 {
    #[inline]
    fn approx_epsilon(_: Option<f32>) -> f32 {
        1.0e-6
    }

    #[inline]
    fn approx_eq_eps(a: &f32, b: &f32, epsilon: &f32) -> bool {
        (*a - *b).abs() < *epsilon
    }
}

impl ApproxEq<f64> for f64 {
    #[inline]
    fn approx_epsilon(_: Option<f64>) -> f64 {
        1.0e-6
    }

    #[inline]
    fn approx_eq_eps(a: &f64, b: &f64, approx_epsilon: &f64) -> bool {
        (*a - *b).abs() < *approx_epsilon
    }
}

/// Trait of objects having an absolute value.
/// This is useful if the object does not have the same type as its absolute value.
pub trait Absolute<A> {
    /// Computes some absolute value of this object.
    /// Typically, this will make all component of a matrix or vector positive.
    fn abs(&Self) -> A;
}

/// Trait of objects having an inverse. Typically used to implement matrix inverse.
pub trait Inv {
    /// Returns the inverse of `self`.
    fn inv_cpy(m: &Self) -> Option<Self>;

    /// In-place version of `inverse`.
    fn inv(&mut self) -> bool;
}

/// Trait of objects which can be transposed.
pub trait Transpose {
    /// Computes the transpose of a matrix.
    fn transpose_cpy(m: &Self) -> Self;

    /// In-place version of `transposed`.
    fn transpose(&mut self);
}

/// Traits of objects having an outer product.
pub trait Outer<M> {
    /// Computes the outer product: `self * other`
    fn outer(a: &Self, b: &Self) -> M;
}

/// Trait for computing the covariance of a set of data.
pub trait Cov<M> {
    /// Computes the covariance of the obsevations stored by `self`:
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn cov(&Self) -> M;

    /// Computes the covariance of the obsevations stored by `self`:
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn cov_to(m: &Self, out: &mut M) {
        *out = Cov::cov(m)
    }
}

/// Trait for computing the covariance of a set of data.
pub trait Mean<N> {
    /// Computes the mean of the observations stored by `self`.
    ///
    ///   * For matrices, observations are stored in its rows.
    ///   * For vectors, observations are stored in its components (thus are 1-dimensional).
    fn mean(&Self) -> N;
}

// /// Cholesky decomposition.
// pub trait Chol {
//     /// Performs the cholesky decomposition on `self`. The resulting upper-triangular matrix is
//     /// returned. Returns `None` if the matrix is not symetric positive-definite.
//     fn chol(self) -> Option<Self>;
//
//     /// Performs the cholesky decomposition on `self`. The resulting upper-triangular matrix is
//     /// written to a given parameter. If the decomposition fails `false` is returned; the state of
//     /// the output parameter is undefined.
//     fn chol_to(&self, out: &mut Self) -> bool {
//         match self.chol() {
//             None => false,
//             Some(decomp) => {
//                 *out = decomp;
//                 true
//             }
//         }
//     }
// }

// XXX: those two traits should not exist since there is generalized operator overloading of Add
// and Sub.
// However, using the same trait multiple time as a trait bound (ex: impl<T: Add<N, V> + Add<V, V>)
// does not work properly, mainly because the way we are doing generalized operator overloading is
// verry hacky.
//
// Hopefully, this will be fixed on a future version of the language!
/// Trait of objects having a right multiplication with another element.
pub trait RMul<V> {
    /// Computes `self * v`
    fn rmul(&self, v: &V) -> V;
}

impl<M: Mul<T, T>, T> RMul<T> for M {
    fn rmul(&self, v: &T) -> T {
        self * *v
    }
}

/// Trait of objects having a left multiplication with another element.
pub trait LMul<V> {
    /// Computes `v * self`
    fn lmul(&self, &V) -> V;
}

impl<T: Mul<M, T>, M> LMul<T> for M {
    fn lmul(&self, v: &T) -> T {
        v * *self
    }
}

// XXX: those traits should not exist since there is generalized operator overloading of Add and
// Sub.
// However, using the same trait multiple time as a trait bound (ex: impl<T: Add<N, V> + Add<V, V>)
// does not work properly, mainly because the way we are doing generalized operator overloading is
// verry hacky.
//
// Hopefully, this will be fixed on a future version of the language!
/// Trait of objects having an addition with a scalar.
pub trait ScalarAdd<N> {
    /// Gets the result of `self + n`.
    fn add_s(&self, n: &N) -> Self;
}

impl<N, T: Add<N, T>> ScalarAdd<N> for T {
    /// Gets the result of `self + n`.
    fn add_s(&self, n: &N) -> T {
        *self + *n
    }
}

/**
 * Trait of objects having a subtraction with a scalar.
 */
pub trait ScalarSub<N> {
    /// Gets the result of `self - n`.
    fn sub_s(&self, n: &N) -> Self;
}

impl<N, T: Sub<N, T>> ScalarSub<N> for T {
    /// Gets the result of `self - n`.
    fn sub_s(&self, n: &N) -> T {
        *self - *n
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate r2d2;
extern crate r2d2_postgres;
extern crate postgres;
extern crate iron;
extern crate persistent;
#[macro_use]
extern crate potboiler_common;
extern crate logger;
extern crate serde_json;
extern crate serde;
extern crate hyper;
extern crate router;
extern crate uuid;
extern crate hybrid_clocks;

use hybrid_clocks::{Timestamp, WallT};
use iron::modifiers::Redirect;
use iron::prelude::{Chain, Iron, IronError, IronResult, Request, Response};
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use potboiler_common::{db, get_raw_timestamp};
use potboiler_common::string_error::StringError;
use potboiler_common::types::Log;
use serde_json::{Map, Value};
use std::env;
use std::io::Cursor;
use std::ops::Deref;
use types::QueueOperation;
use uuid::Uuid;

mod types;

pub type PostgresConnection = r2d2::PooledConnection<r2d2_postgres::PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
    static ref HOST: String = env::var("HOST").expect("Needed HOST");
}

fn iron_str_error<T: std::error::Error + std::marker::Send + 'static>(se: T) -> iron::IronError {
    let desc = format!("{:?}", se);
    return IronError::new(se, (status::BadRequest, desc));
}

fn string_from_body<T: std::io::Read>(body: &mut T) -> IronResult<String> {
    let mut result = String::new();
    try!(body.read_to_string(&mut result).map_err(iron_str_error));
    Ok(result)
}

fn json_from_body(req: &mut Request) -> IronResult<Value> {
    let body_string = try!(string_from_body(&mut req.body));
    let json: Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    return Ok(json);
}

fn add_queue_operation(op: QueueOperation) -> IronResult<String> {
    let client = hyper::client::Client::new();
    let mut res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(&op).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    let data = try!(string_from_body(&mut res));
    Ok(data)
}

fn create_queue(req: &mut Request) -> IronResult<Response> {
    let json = try!(json_from_body(req));
    let op = try!(serde_json::from_value::<types::QueueCreate>(json).map_err(iron_str_error));
    let name = op.name.clone();
    match add_queue_operation(QueueOperation::Create(op)) {
        Ok(_) => {
            let new_url = format!("http://{}:8000/queue/{}", HOST.deref(), &name);
            Ok(Response::with((status::Created,
                               Redirect(iron::Url::parse(&new_url).expect("URL parsed ok")))))
        }
        Err(val) => Err(val),
    }
}

fn row_to_state(row: &postgres::rows::Row) -> IronResult<types::QueueState> {
    let raw_state: String = row.get("state");
    // FIXME: format! bit is a hacky workaround for https://github.com/serde-rs/serde/issues/251
    return serde_json::from_str(&format!("\"{}\"", raw_state)).map_err(iron_str_error);
}

fn parse_progress<F>(req: &mut Request,
                     progress: types::QueueProgress,
                     should_update: F)
                     -> IronResult<Response>
    where F: Fn(&types::QueueState, &Timestamp<WallT>) -> Option<(Timestamp<WallT>, String)>
{
    let conn = get_pg_connection!(&req);
    let results = try!(conn.query(&format!("SELECT task_name, state, hlc_tstamp from {} where id=$1",
                        &progress.queue_name),
               &[&progress.id])
        .map_err(iron_str_error));
    if results.is_empty() {
        return Ok(Response::with((status::NotFound,
                                  format!("No queue item {} in {}", &progress.id, &progress.queue_name))));
    } else {
        let row = results.get(0);
        let state = try!(row_to_state(&row));
        let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
        let when = try!(hybrid_clocks::Timestamp::read_bytes(Cursor::new(hlc_tstamp))
            .map_err(iron_str_error));
        if let Some((log_when, status)) = should_update(&state, &when) {
            let raw_timestamp = get_raw_timestamp(&log_when);
            try!(conn.execute(&format!("UPDATE {} set hlc_tstamp=$1, worker=$2, state=$3 where id=$4",
                                  &progress.queue_name),
                         &[&raw_timestamp, &progress.worker_id, &status, &progress.id])
                .map_err(iron_str_error));
            return Ok(Response::with(status::NoContent));
        } else {
            return Ok(Response::with((status::Conflict, "Out of date change")));
        }
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let json = try!(json_from_body(req));
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let log_when = log.when.clone();
    let op = try!(serde_json::from_value::<QueueOperation>(log.data).map_err(iron_str_error));
    info!("op: {:?}", op);
    let conn = get_pg_connection!(&req);
    match op {
        QueueOperation::Create(create) => {
            info!("create: {:?}", create);
            let trans = conn.transaction().unwrap();
            let qc = types::QueueConfig { timeout_ms: create.timeout_ms };
            trans.execute("INSERT INTO queues (key, config) VALUES($1, $2)",
                         &[&create.name, &serde_json::to_value(&qc)])
                .unwrap();
            trans.execute(&format!("CREATE TABLE IF NOT EXISTS {} (id UUID PRIMARY KEY, task_name \
                                   VARCHAR(2083) NOT NULL, state VARCHAR(8) NOT NULL, info JSONB NOT \
                                   NULL, hlc_tstamp BYTEA NOT NULL, worker UUID NULL)",
                                  &create.name),
                         &[])
                .expect("make particular queue table worked");
            trans.commit().unwrap();
        }
        QueueOperation::Add(add) => {
            info!("add: {:?}", add);
            let raw_timestamp = get_raw_timestamp(&log.when);
            conn.execute(&format!("INSERT INTO {} (id, task_name, state, info, hlc_tstamp) VALUES($1, $2, \
                                   $3, $4, $5)",
                                  add.queue_name),
                         &[&log.id,
                           &add.task_name,
                           &String::from("pending"),
                           &serde_json::to_value(&add.info),
                           &raw_timestamp])
                .unwrap();
        }
        QueueOperation::Progress(progress) => {
            info!("progress: {:?}", progress);
            return parse_progress(req, progress, |state, when| {
                if state == &types::QueueState::Pending ||
                   (state == &types::QueueState::Working && &log_when > when) {
                    Some((log_when, String::from("working")))
                } else {
                    None
                }
            });
        }
        QueueOperation::Done(done) => {
            info!("done: {:?}", done);
            return parse_progress(req, done, |state, when| {
                if state != &types::QueueState::Done ||
                   (state == &types::QueueState::Done && &log_when > when) {
                    Some((log_when, String::from("done")))
                } else {
                    None
                }
            });
        }
        QueueOperation::Delete(_) => {
            warn!("Haven't implemented queue delete yet");
            return Ok(Response::with(status::BadRequest));
        }
    };
    Ok(Response::with(status::NoContent))
}

fn get_req_key_with_iron_err(req: &mut Request, key: &str) -> IronResult<String> {
    Ok(try!(potboiler_common::get_req_key(req, key)
        .ok_or(iron_str_error(StringError::from(format!("No {}", key))))))
}

fn get_queue_name(req: &mut Request) -> IronResult<String> {
    get_req_key_with_iron_err(req, "queue_name")
}

fn get_queue_items(req: &mut Request) -> IronResult<Response> {
    let conn = get_pg_connection!(&req);
    let queue_name = try!(get_queue_name(req));
    let results = try!(conn.query(&format!("select id, task_name, state from {}", &queue_name),
               &[])
        .map_err(iron_str_error));
    let mut queue = Map::new();
    for row in &results {
        let id: Uuid = row.get("id");
        let state = try!(row_to_state(&row));
        if state == types::QueueState::Done {
            continue;
        }
        let item = types::QueueListItem {
            task_name: row.get("task_name"),
            state: state,
        };
        queue.insert(id.to_string(), serde_json::to_value(&item));
    }
    let value = Value::Object(queue);
    Ok(Response::with((status::Ok, serde_json::ser::to_string(&value).unwrap())))
}

fn get_item_id(req: &mut Request) -> IronResult<Uuid> {
    Uuid::parse_str(&try!(get_req_key_with_iron_err(req, "id"))).map_err(iron_str_error)
}

fn get_queue_item(req: &mut Request) -> IronResult<Response> {
    let conn = get_pg_connection!(&req);
    let queue_name = try!(get_queue_name(req));
    let id = try!(get_item_id(req));
    let results = try!(conn.query(&format!("select task_name, state, info, worker from {} where id=$1",
                        &queue_name),
               &[&id])
        .map_err(iron_str_error));
    if results.is_empty() {
        Ok(Response::with((status::NotFound, format!("No queue item {} in {}", id, queue_name))))
    } else {
        let row = results.get(0);
        let item = types::QueueItem {
            task_name: row.get("task_name"),
            state: try!(row_to_state(&row)),
            info: row.get("info"),
            worker: row.get("worker"),
        };
        Ok(Response::with((status::Ok, serde_json::to_string(&item).unwrap())))
    }
}

fn add_queue_item(req: &mut Request) -> IronResult<Response> {
    let mut json = try!(json_from_body(req));
    {
        let queue_name = try!(get_queue_name(req));
        let map = json.as_object_mut().unwrap();
        map.insert("queue_name".to_string(), serde_json::to_value(&queue_name));
    }
    let op = try!(serde_json::from_value::<types::QueueAdd>(json).map_err(iron_str_error));
    match add_queue_operation(QueueOperation::Add(op)) {
        Ok(_) => Ok(Response::with(status::Created)),
        Err(val) => Err(val),
    }
}

fn build_queue_progress(req: &mut Request) -> IronResult<types::QueueProgress> {
    let mut json = try!(json_from_body(req));
    {
        let queue_name = try!(get_queue_name(req));
        let id = try!(get_item_id(req));
        let map = json.as_object_mut().unwrap();
        map.insert("queue_name".to_string(), serde_json::to_value(&queue_name));
        map.insert("id".to_string(), serde_json::to_value(&id));
    }
    return Ok(try!(serde_json::from_value::<types::QueueProgress>(json).map_err(iron_str_error)));
}

fn progress_queue_item(req: &mut Request) -> IronResult<Response> {
    let op = try!(build_queue_progress(req));
    match add_queue_operation(QueueOperation::Progress(op)) {
        Ok(_) => Ok(Response::with(status::Ok)),
        Err(val) => Err(val),
    }
}

fn finish_queue_item(req: &mut Request) -> IronResult<Response> {
    let op = try!(build_queue_progress(req));
    match add_queue_operation(QueueOperation::Done(op)) {
        Ok(_) => Ok(Response::with(status::Ok)),
        Err(val) => Err(val),
    }
}

fn make_queue_table(conn: &PostgresConnection) {
    conn.execute("CREATE TABLE IF NOT EXISTS queues (key VARCHAR(1024) PRIMARY KEY, config JSONB NOT NULL)",
                 &[])
        .expect("make queue table worked");
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    map.insert("url".to_string(),
               format!("http://{}:8000/event", HOST.deref()).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    make_queue_table(&conn);
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = router::Router::new();
    router.post("/create", create_queue);
    router.post("/event", new_event);
    router.get("/queue/:queue_name", get_queue_items);
    router.post("/queue/:queue_name", add_queue_item);
    router.get("/queue/:queue_name/:id", get_queue_item);
    router.put("/queue/:queue_name/:id", progress_queue_item);
    router.delete("/queue/:queue_name/:id", finish_queue_item);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link(PRead::<db::PostgresDB>::both(pool));
    info!("Pigtail booted");
    Iron::new(chain).http("0.0.0.0:8000").unwrap();
}
<commit_msg>Add "delete queue" to Pigtail<commit_after>#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate r2d2;
extern crate r2d2_postgres;
extern crate postgres;
extern crate iron;
extern crate persistent;
#[macro_use]
extern crate potboiler_common;
extern crate logger;
extern crate serde_json;
extern crate serde;
extern crate hyper;
extern crate router;
extern crate uuid;
extern crate hybrid_clocks;

use hybrid_clocks::{Timestamp, WallT};
use iron::modifiers::Redirect;
use iron::prelude::{Chain, Iron, IronError, IronResult, Request, Response};
use iron::status;
use logger::Logger;
use persistent::Read as PRead;
use potboiler_common::{db, get_raw_timestamp};
use potboiler_common::string_error::StringError;
use potboiler_common::types::Log;
use serde_json::{Map, Value};
use std::env;
use std::io::Cursor;
use std::ops::Deref;
use types::QueueOperation;
use uuid::Uuid;

mod types;

pub type PostgresConnection = r2d2::PooledConnection<r2d2_postgres::PostgresConnectionManager>;

lazy_static! {
    static ref SERVER_URL: String = env::var("SERVER_URL").expect("Needed SERVER_URL");
    static ref HOST: String = env::var("HOST").expect("Needed HOST");
}

fn iron_str_error<T: std::error::Error + std::marker::Send + 'static>(se: T) -> iron::IronError {
    let desc = format!("{:?}", se);
    return IronError::new(se, (status::BadRequest, desc));
}

fn string_from_body<T: std::io::Read>(body: &mut T) -> IronResult<String> {
    let mut result = String::new();
    try!(body.read_to_string(&mut result).map_err(iron_str_error));
    Ok(result)
}

fn json_from_body(req: &mut Request) -> IronResult<Value> {
    let body_string = try!(string_from_body(&mut req.body));
    let json: Value = match serde_json::de::from_str(&body_string) {
        Ok(val) => val,
        Err(err) => return Err(IronError::new(err, (status::BadRequest, "Bad JSON"))),
    };
    return Ok(json);
}

fn add_queue_operation(op: QueueOperation) -> IronResult<String> {
    let client = hyper::client::Client::new();
    let mut res = client.post(SERVER_URL.deref())
        .body(&serde_json::ser::to_string(&op).unwrap())
        .send()
        .expect("sender ok");
    assert_eq!(res.status, hyper::status::StatusCode::Created);
    let data = try!(string_from_body(&mut res));
    Ok(data)
}

fn create_queue(req: &mut Request) -> IronResult<Response> {
    let json = try!(json_from_body(req));
    let op = try!(serde_json::from_value::<types::QueueCreate>(json).map_err(iron_str_error));
    let name = op.name.clone();
    match add_queue_operation(QueueOperation::Create(op)) {
        Ok(_) => {
            let new_url = format!("http://{}:8000/queue/{}", HOST.deref(), &name);
            Ok(Response::with((status::Created,
                               Redirect(iron::Url::parse(&new_url).expect("URL parsed ok")))))
        }
        Err(val) => Err(val),
    }
}

fn delete_queue(req: &mut Request) -> IronResult<Response> {
    let queue_name = try!(get_queue_name(req));
    try!(add_queue_operation(QueueOperation::Delete(queue_name)));
    Ok(Response::with(status::Ok))
}

fn row_to_state(row: &postgres::rows::Row) -> IronResult<types::QueueState> {
    let raw_state: String = row.get("state");
    // FIXME: format! bit is a hacky workaround for https://github.com/serde-rs/serde/issues/251
    return serde_json::from_str(&format!("\"{}\"", raw_state)).map_err(iron_str_error);
}

fn parse_progress<F>(req: &mut Request,
                     progress: types::QueueProgress,
                     should_update: F)
                     -> IronResult<Response>
    where F: Fn(&types::QueueState, &Timestamp<WallT>) -> Option<(Timestamp<WallT>, String)>
{
    let conn = get_pg_connection!(&req);
    let results = try!(conn.query(&format!("SELECT task_name, state, hlc_tstamp from {} where id=$1",
                        &progress.queue_name),
               &[&progress.id])
        .map_err(iron_str_error));
    if results.is_empty() {
        return Ok(Response::with((status::NotFound,
                                  format!("No queue item {} in {}", &progress.id, &progress.queue_name))));
    } else {
        let row = results.get(0);
        let state = try!(row_to_state(&row));
        let hlc_tstamp: Vec<u8> = row.get("hlc_tstamp");
        let when = try!(hybrid_clocks::Timestamp::read_bytes(Cursor::new(hlc_tstamp))
            .map_err(iron_str_error));
        if let Some((log_when, status)) = should_update(&state, &when) {
            let raw_timestamp = get_raw_timestamp(&log_when);
            try!(conn.execute(&format!("UPDATE {} set hlc_tstamp=$1, worker=$2, state=$3 where id=$4",
                                  &progress.queue_name),
                         &[&raw_timestamp, &progress.worker_id, &status, &progress.id])
                .map_err(iron_str_error));
            return Ok(Response::with(status::NoContent));
        } else {
            return Ok(Response::with((status::Conflict, "Out of date change")));
        }
    }
}

fn new_event(req: &mut Request) -> IronResult<Response> {
    let json = try!(json_from_body(req));
    let log = try!(serde_json::from_value::<Log>(json).map_err(iron_str_error));
    info!("log: {:?}", log);
    let log_when = log.when.clone();
    let op = try!(serde_json::from_value::<QueueOperation>(log.data).map_err(iron_str_error));
    info!("op: {:?}", op);
    let conn = get_pg_connection!(&req);
    match op {
        QueueOperation::Create(create) => {
            info!("create: {:?}", create);
            let trans = conn.transaction().unwrap();
            let qc = types::QueueConfig { timeout_ms: create.timeout_ms };
            trans.execute("INSERT INTO queues (key, config) VALUES($1, $2)",
                         &[&create.name, &serde_json::to_value(&qc)])
                .unwrap();
            trans.execute(&format!("CREATE TABLE IF NOT EXISTS {} (id UUID PRIMARY KEY, task_name \
                                   VARCHAR(2083) NOT NULL, state VARCHAR(8) NOT NULL, info JSONB NOT \
                                   NULL, hlc_tstamp BYTEA NOT NULL, worker UUID NULL)",
                                  &create.name),
                         &[])
                .expect("make particular queue table worked");
            trans.commit().unwrap();
        }
        QueueOperation::Add(add) => {
            info!("add: {:?}", add);
            let raw_timestamp = get_raw_timestamp(&log.when);
            conn.execute(&format!("INSERT INTO {} (id, task_name, state, info, hlc_tstamp) VALUES($1, $2, \
                                   $3, $4, $5)",
                                  add.queue_name),
                         &[&log.id,
                           &add.task_name,
                           &String::from("pending"),
                           &serde_json::to_value(&add.info),
                           &raw_timestamp])
                .unwrap();
        }
        QueueOperation::Progress(progress) => {
            info!("progress: {:?}", progress);
            return parse_progress(req, progress, |state, when| {
                if state == &types::QueueState::Pending ||
                   (state == &types::QueueState::Working && &log_when > when) {
                    Some((log_when, String::from("working")))
                } else {
                    None
                }
            });
        }
        QueueOperation::Done(done) => {
            info!("done: {:?}", done);
            return parse_progress(req, done, |state, when| {
                if state != &types::QueueState::Done ||
                   (state == &types::QueueState::Done && &log_when > when) {
                    Some((log_when, String::from("done")))
                } else {
                    None
                }
            });
        }
        QueueOperation::Delete(queue_name) => {
            let trans = try!(conn.transaction().map_err(iron_str_error));
            try!(trans.execute(&format!("DROP TABLE IF EXISTS {}", queue_name), &[]).map_err(iron_str_error));
            try!(trans.execute("DELETE FROM queues where key=$1", &[&queue_name]).map_err(iron_str_error));
            try!(trans.commit().map_err(iron_str_error));
        }
    };
    Ok(Response::with(status::NoContent))
}

fn get_req_key_with_iron_err(req: &mut Request, key: &str) -> IronResult<String> {
    Ok(try!(potboiler_common::get_req_key(req, key)
        .ok_or(iron_str_error(StringError::from(format!("No {}", key))))))
}

fn get_queue_name(req: &mut Request) -> IronResult<String> {
    get_req_key_with_iron_err(req, "queue_name")
}

fn get_queue_items(req: &mut Request) -> IronResult<Response> {
    let conn = get_pg_connection!(&req);
    let queue_name = try!(get_queue_name(req));
    let results = try!(conn.query(&format!("select id, task_name, state from {}", &queue_name),
               &[])
        .map_err(iron_str_error));
    let mut queue = Map::new();
    for row in &results {
        let id: Uuid = row.get("id");
        let state = try!(row_to_state(&row));
        if state == types::QueueState::Done {
            continue;
        }
        let item = types::QueueListItem {
            task_name: row.get("task_name"),
            state: state,
        };
        queue.insert(id.to_string(), serde_json::to_value(&item));
    }
    let value = Value::Object(queue);
    Ok(Response::with((status::Ok, serde_json::ser::to_string(&value).unwrap())))
}

fn get_item_id(req: &mut Request) -> IronResult<Uuid> {
    Uuid::parse_str(&try!(get_req_key_with_iron_err(req, "id"))).map_err(iron_str_error)
}

fn get_queue_item(req: &mut Request) -> IronResult<Response> {
    let conn = get_pg_connection!(&req);
    let queue_name = try!(get_queue_name(req));
    let id = try!(get_item_id(req));
    let results = try!(conn.query(&format!("select task_name, state, info, worker from {} where id=$1",
                        &queue_name),
               &[&id])
        .map_err(iron_str_error));
    if results.is_empty() {
        Ok(Response::with((status::NotFound, format!("No queue item {} in {}", id, queue_name))))
    } else {
        let row = results.get(0);
        let item = types::QueueItem {
            task_name: row.get("task_name"),
            state: try!(row_to_state(&row)),
            info: row.get("info"),
            worker: row.get("worker"),
        };
        Ok(Response::with((status::Ok, serde_json::to_string(&item).unwrap())))
    }
}

fn add_queue_item(req: &mut Request) -> IronResult<Response> {
    let mut json = try!(json_from_body(req));
    {
        let queue_name = try!(get_queue_name(req));
        let map = json.as_object_mut().unwrap();
        map.insert("queue_name".to_string(), serde_json::to_value(&queue_name));
    }
    let op = try!(serde_json::from_value::<types::QueueAdd>(json).map_err(iron_str_error));
    match add_queue_operation(QueueOperation::Add(op)) {
        Ok(_) => Ok(Response::with(status::Created)),
        Err(val) => Err(val),
    }
}

fn build_queue_progress(req: &mut Request) -> IronResult<types::QueueProgress> {
    let mut json = try!(json_from_body(req));
    {
        let queue_name = try!(get_queue_name(req));
        let id = try!(get_item_id(req));
        let map = json.as_object_mut().unwrap();
        map.insert("queue_name".to_string(), serde_json::to_value(&queue_name));
        map.insert("id".to_string(), serde_json::to_value(&id));
    }
    return Ok(try!(serde_json::from_value::<types::QueueProgress>(json).map_err(iron_str_error)));
}

fn progress_queue_item(req: &mut Request) -> IronResult<Response> {
    let op = try!(build_queue_progress(req));
    match add_queue_operation(QueueOperation::Progress(op)) {
        Ok(_) => Ok(Response::with(status::Ok)),
        Err(val) => Err(val),
    }
}

fn finish_queue_item(req: &mut Request) -> IronResult<Response> {
    let op = try!(build_queue_progress(req));
    match add_queue_operation(QueueOperation::Done(op)) {
        Ok(_) => Ok(Response::with(status::Ok)),
        Err(val) => Err(val),
    }
}

fn make_queue_table(conn: &PostgresConnection) {
    conn.execute("CREATE TABLE IF NOT EXISTS queues (key VARCHAR(1024) PRIMARY KEY, config JSONB NOT NULL)",
                 &[])
        .expect("make queue table worked");
}

fn main() {
    log4rs::init_file("log.yaml", Default::default()).expect("log config ok");
    let client = hyper::client::Client::new();

    let mut map: serde_json::Map<String, String> = serde_json::Map::new();
    map.insert("url".to_string(),
               format!("http://{}:8000/event", HOST.deref()).to_string());
    let res = client.post(&format!("{}/register", SERVER_URL.deref()))
        .body(&serde_json::ser::to_string(&map).unwrap())
        .send()
        .expect("Register ok");
    assert_eq!(res.status, hyper::status::StatusCode::NoContent);

    let db_url: &str = &env::var("DATABASE_URL").expect("Needed DATABASE_URL");
    let pool = db::get_pool(db_url);
    let conn = pool.get().unwrap();
    make_queue_table(&conn);
    let (logger_before, logger_after) = Logger::new(None);
    let mut router = router::Router::new();
    router.post("/create", create_queue);
    router.post("/event", new_event);
    router.get("/queue/:queue_name", get_queue_items);
    router.post("/queue/:queue_name", add_queue_item);
    router.delete("/queue/:queue_name", delete_queue);
    router.get("/queue/:queue_name/:id", get_queue_item);
    router.put("/queue/:queue_name/:id", progress_queue_item);
    router.delete("/queue/:queue_name/:id", finish_queue_item);
    let mut chain = Chain::new(router);
    chain.link_before(logger_before);
    chain.link_after(logger_after);
    chain.link(PRead::<db::PostgresDB>::both(pool));
    info!("Pigtail booted");
    Iron::new(chain).http("0.0.0.0:8000").unwrap();
}
<|endoftext|>


<commit_before>//! Configuration for the zwreec compiler.
//!
//! Uses the `getopts` library to append a `getopts::Options` with compiler
//! specific flags and then creates a `Config` struct from `getopts::Matches`.
//!
//! # Usage
//!
//! This module depends on the [getopts](http://doc.rust-lang.org/getopts/getopts/index.html)
//! crate. To use it, you need to add `getopts` to your project. Read getopts'
//! [Usage](http://doc.rust-lang.org/getopts/getopts/index.html#usage) for more information.
//!
//! # Example
//!
//! The following example shows how to generate a `Config` struct from an empty
//! `getopts::Options`.
//!
//! ```
//! extern crate zwreec;
//! extern crate getopts;
//!
//! use zwreec::config;
//! use zwreec::config::Config;
//!
//! fn main() {
//!     let args: Vec<String> = std::env::args().collect();
//!     let opts = config::zwreec_options(getopts::Options::new());
//!
//!     let matches = match opts.parse(&args[1..]) {
//!         Ok(m) => m,
//!         Err(f) => { panic!(f.to_string()) }
//!     }
//!
//!     let cfg = Config::from_matches(&matches);
//! }
//! ```
//!
//! # Appending this module (Development)
//!
//! To add new compiler flags for the compiler, you need to change three parts
//! in this module.
//!
//! 1. Appending the Struct
//!
//!     To store your new setting, you need to add it as a field to the `Config` struct. Here we
//!     add a new boolean `pub italics: bool` to control *italics*. Always add a field-comment.
//!
//!     ```
//! pub struct Config {
//!     /// Add easter egg to compiler
//!     pub easter_egg: bool,
//!     /// Instruct compiler to run these test-cases
//!     pub test_cases: Vec<TestCase>,
//!     /// Enables or disalbes Italics
//!     pub italics: bool,
//! }
//!     ```
//!
//!     Now you also need to change `Config::default_config()` to provide the
//!     default value for your new field:
//!
//!     ```
//! pub fn default_config() -> Config {
//!     Config{
//!         easter_egg: true,
//!         test_cases: Vec::new(),
//!         italics: true,
//!     }
//! }
//!     ```
//!
//! 2. Parse your flag in `from_matches()`
//!
//!     `from_matches` uses the Options `-F` and `-N` to enable or disable boolean flags.
//!     Therefore, if you added a new boolean flag, you can now simply add it to the match
//!     statement.
//!
//!     ```
//!     for s in matches.opt_strs("F") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                  cfg.easter_egg = true;
//!                  debug!("enabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = true;
//!                 debug!("enabbled italics");
//!             },
//!             _ => {
//!                 error!("Cannot enable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!
//!     for s in matches.opt_strs("N") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                 cfg.easter_egg = false;
//!                 debug!("disabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = false;
//!                 debug:("disalbed italics");
//!             }
//!             _ => {
//!                 error!("Cannot disable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!     ```
//!
//! 3. Adding something that is not a boolean
//!
//!     If you need to add an option that is *not* a boolean, you will need to append the struct
//!     and `default_config()` accordingly (e.g. `pub notaflag: String,`). Then you need to add a new
//!     `getopts::Option` to `zwreec_options()`:
//!
//!     ```
//! pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
//!     opts.optmulti("F", "feature", "", "FEAT");
//!     opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
//!                         List of supported features (default):
//!                             easter-egg (enabled)", "FEAT");
//!     opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
//!
//!     opts.optopt("n", "notaflag", "notaflag", "SOMETHING");
//!
//!     opts
//! }
//!     ```
//!
//!     Now you can append `Config::from_matches()` to analyse the provided matches for your new
//!     option and set the `Config` accordingly:
//!
//!     ```
//!     if let s = matches.opt_str("n") {
//!         cfg.notaflag = s;
//!     }
//!     ```
//!
//! 4. Now you can use the new flag inside the compiler.
use getopts;

use std::vec::Vec;

/// Represents the configuration for the compiler.
///
/// This struct is created using either `config::default_config()` or
/// `config::parse_matches(matches: &getopts::Matches)`. It contains a series
/// of fields that can be used to control the behaviour of the compiler.
///
/// # Example Usage inside the compiler
///
/// ```
/// let cfg = Config::default_config();
///
/// if cfg.easter_egg {
///     println!("Egg!");
/// }
/// ```
pub struct Config {
    /// Add easter egg to compiler
    pub easter_egg: bool,
    /// Instruct compiler to run these test-cases
    pub test_cases: Vec<TestCase>,
}

impl Config {
    /// Returns a `Config` struct with preset defaults
    pub fn default_config() -> Config {
        Config{
            easter_egg: true,
            test_cases: Vec::new(),
        }
    }

    /// Returns a `Config` struct by using `getopts::Matches` to set the fields.
    ///
    /// This method analyses a `getopts::Matches` for fields provided by
    /// `zwreec_options()`.
    ///
    /// # Example
    ///
    /// ```
    /// let args: Vec<String> = std::env::args().collect();
    /// let opts = config::zwreec_options(getopts::Options::new());
    ///
    /// let matches = match opts.parse(&args[1..]) {
    ///     Ok(m) => m,
    ///     Err(f) => { panic!(f.to_string()) }
    /// }
    ///
    /// let cfg = Config::from_matches(&matches);
    /// ```
    pub fn from_matches(matches: &getopts::Matches) -> Config {
        // load defaults
        let mut cfg = Config::default_config();

        if matches.opt_present("generate-sample-zcode") {
            cfg.test_cases.push(TestCase::ZcodeBackend);
        }

        // TODO: Find a way to make these two loops somewhat less.. repetitive
        for s in matches.opt_strs("F") {
            match s.as_ref() {
                "easter-egg" => {
                     cfg.easter_egg = true;
                     debug!("enabled easter-egg");
                },
                _ => {
                    error!("Cannot enable feature {} - feature not known.", s);
                }
            }
        }

        for s in matches.opt_strs("N") {
            match s.as_ref() {
                "easter-egg" => {
                    cfg.easter_egg = false;
                    debug!("disabled easter-egg");
                },
                _ => {
                    error!("Cannot disable feature {} - feature not known.", s);
                }
            }
        }

        cfg
    }
}

// TODO: If this stays only one Test Case, enum should be removed
/// The Type used to define backend tests for the compiler
#[derive(PartialEq)]
pub enum TestCase {
    /// Skips the normal compiler chain and builds an example zcode file by
    /// using every opcode.
    ZcodeBackend,
}

/// Appends a `getopts::Options` with compiler specific flags
///
/// The method `Config::from_matches()` looks for very specific `getopts::Matches`.
/// This function takes an `getopts::Options` to append it with Options required
/// by `from_matches`. It currently adds three fields:
///
/// ```
/// # let mut opts = getopts::Options::new();
/// opts.optmulti("F", "feature", "", "FEAT");
/// opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
///                     List of supported features (default):
///                         easter-egg (enabled)", "FEAT");
/// opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
/// ```
///
/// # Example
///
/// You can use this function to append your `getopts::Options`.
///
/// ```
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "print this message");
///
/// let opts = config::zwreec_options(opts);
/// ```
///
/// Another useful example is to use it to gernerate a more compact usage by
/// having a function that only returns your options.
///
/// ```
/// fn options() -> getopts::Options {
///     let mut opts = getopts::Options::new();
///     opts.optflag("h", "help", "display this help and exit");
///     opts.optflag("V", "version", "display version");
///
///     opts
/// }
///
/// fn print_usage(program: &str, verbose: bool) {
///     let brief = format!("Usage: {} [options]", program)
///
///     let opts = if verbose {
///         config::zwreec_options(options());
///     } else {
///         options();
///     }
///
///     print!("{}", opts.usage(&brief);
/// }
/// ```
/// As you can see, `options()` returns your own command line options, which are then conditionally
/// expanded by using `zwreec_options()`.
pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
    opts.optmulti("F", "feature", "", "FEAT");
    opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
                        List of supported features (default):
                            easter-egg (enabled)", "FEAT");
    opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");

    opts
}

#[cfg(test)]
mod tests {
    use super::*;
    use getopts;

    fn config_from_args(args: Vec<String>) -> Config {
        let opts = zwreec_options(getopts::Options::new());

        match opts.parse(&args) {
            Ok(m) => Config::from_matches(&m),
            Err(f) => { panic!(f.to_string()) }
        }
    }

    #[test]
    fn test_feature_easter_egg_true() {
        let cfg = config_from_args(vec!["-F".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, true);
    }

    #[test]
    fn test_feature_easter_egg_false() {
        let cfg = config_from_args(vec!["-N".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_feature_easter_egg_both() {
        let cfg = config_from_args(vec![
                                   "-N".to_string(),
                                   "easter-egg".to_string(),
                                   "-F".to_string(),
                                   "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_generate_sample_zcode() {
        let cfg = config_from_args(vec!["-e".to_string()]);

        assert_eq!(cfg.test_cases.is_empty(), false);

        let mut contains = false;
        for tc in cfg.test_cases {
            if tc == TestCase::ZcodeBackend {
                contains = true;
            }
        }

        assert!(contains);
    }
}
<commit_msg>Fix documentation test for config mod<commit_after>//! Configuration for the zwreec compiler.
//!
//! Uses the `getopts` library to append a `getopts::Options` with compiler
//! specific flags and then creates a `Config` struct from `getopts::Matches`.
//!
//! # Usage
//!
//! This module depends on the [getopts](http://doc.rust-lang.org/getopts/getopts/index.html)
//! crate. To use it, you need to add `getopts` to your project. Read getopts'
//! [Usage](http://doc.rust-lang.org/getopts/getopts/index.html#usage) for more information.
//!
//! # Example
//!
//! The following example shows how to generate a `Config` struct from an empty
//! `getopts::Options`.
//!
//! ```
//! extern crate zwreec;
//! extern crate getopts;
//!
//! use zwreec::config;
//! use zwreec::config::Config;
//!
//!
//! fn main() {
//!     let args: Vec<String> = std::env::args().collect();
//!     let opts = config::zwreec_options(getopts::Options::new());
//!
//!     let matches = match opts.parse(&args[1..]) {
//!         Ok(m) => m,
//!         Err(f) => { panic!(f.to_string()) }
//!     };
//!
//!     let cfg = Config::from_matches(&matches);
//! }
//! ```
//!
//! # Appending this module (Development)
//!
//! To add new compiler flags for the compiler, you need to change three parts
//! in this module.
//!
//! 1. Appending the Struct
//!
//!     To store your new setting, you need to add it as a field to the `Config` struct. Here we
//!     add a new boolean `pub italics: bool` to control *italics*. Always add a field-comment.
//!
//!     ```ignore
//!     pub struct Config {
//!         /// Add easter egg to compiler
//!         pub easter_egg: bool,
//!         /// Instruct compiler to run these test-cases
//!         pub test_cases: Vec<TestCase>,
//!         /// Enables or disalbes Italics
//!         pub italics: bool,
//!     }
//!     ```
//!
//!     Now you also need to change `Config::default_config()` to provide the
//!     default value for your new field:
//!
//!     ```ignore
//!     pub fn default_config() -> Config {
//!         Config{
//!             easter_egg: true,
//!             test_cases: Vec::new(),
//!             italics: true,
//!         }
//!     }
//!     ```
//!
//! 2. Parse your flag in `from_matches()`
//!
//!     `from_matches` uses the Options `-F` and `-N` to enable or disable boolean flags.
//!     Therefore, if you added a new boolean flag, you can now simply add it to the match
//!     statement.
//!
//!     ```ignore
//!     for s in matches.opt_strs("F") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                  cfg.easter_egg = true;
//!                  debug!("enabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = true;
//!                 debug!("enabbled italics");
//!             },
//!             _ => {
//!                 error!("Cannot enable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!
//!     for s in matches.opt_strs("N") {
//!         match s.as_ref() {
//!             "easter-egg" => {
//!                 cfg.easter_egg = false;
//!                 debug!("disabled easter-egg");
//!             },
//!             "italics" => {
//!                 cfg.italics = false;
//!                 debug:("disalbed italics");
//!             }
//!             _ => {
//!                 error!("Cannot disable feature {} - feature not known.", s);
//!             }
//!         }
//!     }
//!     ```
//!
//! 3. Adding something that is not a boolean
//!
//!     If you need to add an option that is *not* a boolean, you will need to append the struct
//!     and `default_config()` accordingly (e.g. `pub notaflag: String,`). Then you need to add a new
//!     `getopts::Option` to `zwreec_options()`:
//!
//!     ```ignore
//! pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
//!     opts.optmulti("F", "feature", "", "FEAT");
//!     opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
//!                         List of supported features (default):
//!                             easter-egg (enabled)", "FEAT");
//!     opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
//!
//!     opts.optopt("n", "notaflag", "notaflag", "SOMETHING");
//!
//!     opts
//! }
//!     ```
//!
//!     Now you can append `Config::from_matches()` to analyse the provided matches for your new
//!     option and set the `Config` accordingly:
//!
//!     ```ignore
//!     if let s = matches.opt_str("n") {
//!         cfg.notaflag = s;
//!     }
//!     ```
//!
//! 4. Now you can use the new flag inside the compiler.
use getopts;

use std::vec::Vec;

/// Represents the configuration for the compiler.
///
/// This struct is created using either `config::default_config()` or
/// `config::parse_matches(matches: &getopts::Matches)`. It contains a series
/// of fields that can be used to control the behaviour of the compiler.
///
/// # Example Usage inside the compiler
///
/// ```
/// # use zwreec::config;
/// let cfg = config::Config::default_config();
///
/// if cfg.easter_egg {
///     println!("Egg!");
/// }
/// ```
pub struct Config {
    /// Add easter egg to compiler
    pub easter_egg: bool,
    /// Instruct compiler to run these test-cases
    pub test_cases: Vec<TestCase>,
}

impl Config {
    /// Returns a `Config` struct with preset defaults
    ///
    /// # Example
    ///
    /// ```
    /// use zwreec::config::Config;
    /// let cfg = Config::default_config();
    /// ```
    pub fn default_config() -> Config {
        Config{
            easter_egg: true,
            test_cases: Vec::new(),
        }
    }

    /// Returns a `Config` struct by using `getopts::Matches` to set the fields.
    ///
    /// This method analyses a `getopts::Matches` for fields provided by
    /// `zwreec_options()`.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate getopts;
    /// # extern crate zwreec;
    /// let args: Vec<String> = std::env::args().collect();
    /// let opts = zwreec::config::zwreec_options(getopts::Options::new());
    ///
    /// let matches = match opts.parse(&args[1..]) {
    ///     Ok(m) => m,
    ///     Err(f) => { panic!(f.to_string()) }
    /// };
    ///
    /// let cfg = zwreec::config::Config::from_matches(&matches);
    /// ```
    pub fn from_matches(matches: &getopts::Matches) -> Config {
        // load defaults
        let mut cfg = Config::default_config();

        if matches.opt_present("generate-sample-zcode") {
            cfg.test_cases.push(TestCase::ZcodeBackend);
        }

        // TODO: Find a way to make these two loops somewhat less.. repetitive
        for s in matches.opt_strs("F") {
            match s.as_ref() {
                "easter-egg" => {
                     cfg.easter_egg = true;
                     debug!("enabled easter-egg");
                },
                _ => {
                    error!("Cannot enable feature {} - feature not known.", s);
                }
            }
        }

        for s in matches.opt_strs("N") {
            match s.as_ref() {
                "easter-egg" => {
                    cfg.easter_egg = false;
                    debug!("disabled easter-egg");
                },
                _ => {
                    error!("Cannot disable feature {} - feature not known.", s);
                }
            }
        }

        cfg
    }
}

// TODO: If this stays only one Test Case, enum should be removed
/// The Type used to define backend tests for the compiler
#[derive(PartialEq)]
pub enum TestCase {
    /// Skips the normal compiler chain and builds an example zcode file by
    /// using every opcode.
    ZcodeBackend,
}

/// Appends a `getopts::Options` with compiler specific flags
///
/// The method `Config::from_matches()` looks for very specific `getopts::Matches`.
/// This function takes an `getopts::Options` to append it with Options required
/// by `from_matches`. It currently adds three fields:
///
/// ```ignore
/// opts.optmulti("F", "feature", "", "FEAT");
/// opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
///                     List of supported features (default):
///                         easter-egg (enabled)", "FEAT");
/// opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");
/// ```
///
/// # Example
///
/// You can use this function to append your `getopts::Options`.
///
/// ```
/// # extern crate getopts;
/// # extern crate zwreec;
///
/// let mut opts = getopts::Options::new();
/// opts.optflag("h", "help", "print this message");
///
/// let opts = zwreec::config::zwreec_options(opts);
/// ```
///
/// Another useful example is to use it to gernerate a more compact usage by
/// having a function that only returns your options.
///
/// ```
/// # extern crate getopts;
/// # extern crate zwreec;
///
/// fn options() -> getopts::Options {
///     let mut opts = getopts::Options::new();
///     opts.optflag("h", "help", "display this help and exit");
///     opts.optflag("V", "version", "display version");
///
///     opts
/// }
///
/// fn print_usage(program: &str, verbose: bool) {
///     let brief = format!("Usage: {} [options]", program);
///
///     let opts = if verbose {
///         zwreec::config::zwreec_options(options())
///     } else {
///         options()
///     };
///
///     print!("{}", opts.usage(&brief));
/// }
/// ```
/// As you can see, `options()` returns your own command line options, which are then conditionally
/// expanded by using `zwreec_options()`.
pub fn zwreec_options(mut opts: getopts::Options) -> getopts::Options {
    opts.optmulti("F", "feature", "", "FEAT");
    opts.optmulti("N", "no-feature", "enable or disable a feature (can occur multiple times).
                        List of supported features (default):
                            easter-egg (enabled)", "FEAT");
    opts.optflag("e", "generate-sample-zcode", "writes out a sample zcode file, input file is not used and can be omitted");

    opts
}

#[cfg(test)]
mod tests {
    use super::*;
    use getopts;

    fn config_from_args(args: Vec<String>) -> Config {
        let opts = zwreec_options(getopts::Options::new());

        match opts.parse(&args) {
            Ok(m) => Config::from_matches(&m),
            Err(f) => { panic!(f.to_string()) }
        }
    }

    #[test]
    fn test_feature_easter_egg_true() {
        let cfg = config_from_args(vec!["-F".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, true);
    }

    #[test]
    fn test_feature_easter_egg_false() {
        let cfg = config_from_args(vec!["-N".to_string(), "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_feature_easter_egg_both() {
        let cfg = config_from_args(vec![
                                   "-N".to_string(),
                                   "easter-egg".to_string(),
                                   "-F".to_string(),
                                   "easter-egg".to_string()]);

        assert_eq!(cfg.easter_egg, false);
    }

    #[test]
    fn test_generate_sample_zcode() {
        let cfg = config_from_args(vec!["-e".to_string()]);

        assert_eq!(cfg.test_cases.is_empty(), false);

        let mut contains = false;
        for tc in cfg.test_cases {
            if tc == TestCase::ZcodeBackend {
                contains = true;
            }
        }

        assert!(contains);
    }
}
<|endoftext|>