<commit_before>//! The worker module provides the Worker struct which receives instructions from
//! the master, renders and reports back its results

use std::path::PathBuf;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::iter;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};

use scene::Scene;
use film::RenderTarget;
use exec::Config;
use exec::distrib::{Instructions, Frame};

/// Port that the workers listen for the master on
pub static PORT: u16 = 63234;

/// A worker process for distributed rendering. Accepts instructions from
/// the master process telling it what to render, after each frame is finished
/// results are sent back to the master and the next frame is started. Once all
/// frames are finished the worker exits
pub struct Worker {
    instructions: Instructions,
    /// Render target the worker will write the current frame too
    pub render_target: RenderTarget,
    pub scene: Scene,
    pub config: Config,
    /// Our connection to the master
    master: TcpStream,
}

impl Worker {
    /// Listen on the worker `PORT` for the master to contact us
    /// and send us instructions about the scene we should render and
    /// what parts of it we've been assigned
    pub fn listen_for_master(num_threads: u32) -> Worker {
        let (instructions, master) = get_instructions();
        let (scene, rt, spp, mut frame_info) = Scene::load_file(&instructions.scene);
        frame_info.start = instructions.frames.0;
        frame_info.end = instructions.frames.1;
        let config = Config::new(PathBuf::from("/tmp"), instructions.scene.clone(), spp,
                                 num_threads, frame_info,
                                 (instructions.block_start, instructions.block_count));
        Worker { instructions: instructions, render_target: rt, scene: scene,
                 config: config, master: master }
    }
    /// Send our blocks back to the master
    pub fn send_results(&mut self) {
        let (block_size, blocks, pixels) = self.render_target.get_rendered_blocks();
        let frame = Frame::new(self.config.current_frame, block_size, blocks, pixels);
        let bytes = encode(&frame, SizeLimit::Infinite).unwrap();
        match self.master.write_all(&bytes[..]) {
            Err(e) => println!("Failed to send frame to {:?}: {}", self.master, e),
            _ => {},
        }
    }
}

fn get_instructions() -> (Instructions, TcpStream) {
    let listener = TcpListener::bind(("0.0.0.0", PORT)).expect("Worker failed to get port");
    println!("Worker listening for master on {}", PORT);
    match listener.accept() {
        Ok((mut stream, _)) => {
            let mut buf: Vec<_> = iter::repeat(0u8).take(8).collect();
            let mut expected_size = 8;
            let mut currently_read = 0;
            // Read the size header
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            // How many bytes we expect to get from the worker for a frame
            expected_size = decode(&buf[..]).unwrap();
            buf.extend(iter::repeat(0u8).take(expected_size - 8));
            // Now read the rest
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            let instr: Instructions = decode(&buf[..]).unwrap();
            println!("Received instructions: {:?}", instr);
            (instr, stream)
        },
        Err(e) => panic!("Error accepting: {:?}", e),
    }
}

<commit_msg>Workers should panic if they fail to send data to the master<commit_after>//! The worker module provides the Worker struct which receives instructions from
//! the master, renders and reports back its results

use std::path::PathBuf;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::iter;

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};

use scene::Scene;
use film::RenderTarget;
use exec::Config;
use exec::distrib::{Instructions, Frame};

/// Port that the workers listen for the master on
pub static PORT: u16 = 63234;

/// A worker process for distributed rendering. Accepts instructions from
/// the master process telling it what to render, after each frame is finished
/// results are sent back to the master and the next frame is started. Once all
/// frames are finished the worker exits
pub struct Worker {
    instructions: Instructions,
    /// Render target the worker will write the current frame too
    pub render_target: RenderTarget,
    pub scene: Scene,
    pub config: Config,
    /// Our connection to the master
    master: TcpStream,
}

impl Worker {
    /// Listen on the worker `PORT` for the master to contact us
    /// and send us instructions about the scene we should render and
    /// what parts of it we've been assigned
    pub fn listen_for_master(num_threads: u32) -> Worker {
        let (instructions, master) = get_instructions();
        let (scene, rt, spp, mut frame_info) = Scene::load_file(&instructions.scene);
        frame_info.start = instructions.frames.0;
        frame_info.end = instructions.frames.1;
        let config = Config::new(PathBuf::from("/tmp"), instructions.scene.clone(), spp,
                                 num_threads, frame_info,
                                 (instructions.block_start, instructions.block_count));
        Worker { instructions: instructions, render_target: rt, scene: scene,
                 config: config, master: master }
    }
    /// Send our blocks back to the master
    pub fn send_results(&mut self) {
        let (block_size, blocks, pixels) = self.render_target.get_rendered_blocks();
        let frame = Frame::new(self.config.current_frame, block_size, blocks, pixels);
        let bytes = encode(&frame, SizeLimit::Infinite).unwrap();
        match self.master.write_all(&bytes[..]) {
            Err(e) => panic!("Failed to send frame to {:?}: {}", self.master, e),
            _ => {},
        }
    }
}

fn get_instructions() -> (Instructions, TcpStream) {
    let listener = TcpListener::bind(("0.0.0.0", PORT)).expect("Worker failed to get port");
    println!("Worker listening for master on {}", PORT);
    match listener.accept() {
        Ok((mut stream, _)) => {
            let mut buf: Vec<_> = iter::repeat(0u8).take(8).collect();
            let mut expected_size = 8;
            let mut currently_read = 0;
            // Read the size header
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            // How many bytes we expect to get from the worker for a frame
            expected_size = decode(&buf[..]).unwrap();
            buf.extend(iter::repeat(0u8).take(expected_size - 8));
            // Now read the rest
            while currently_read < expected_size {
                match stream.read(&mut buf[currently_read..]) {
                    Ok(n) => currently_read += n,
                    Err(e) => panic!("Failed to read from master, {:?}", e),
                }
            }
            let instr: Instructions = decode(&buf[..]).unwrap();
            println!("Received instructions: {:?}", instr);
            (instr, stream)
        },
        Err(e) => panic!("Error accepting: {:?}", e),
    }
}

<|endoftext|>


<commit_before>use cxx_test_suite::ffi;
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, *ffi::c_return_box());
    ffi::c_return_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, *ffi::c_try_return_box().unwrap());
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(2020)));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared(vec![
        ffi::Shared { z: 1010 },
        ffi::Shared { z: 1011 }
    ]));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![
        ffi::Shared { z: 1010 },
        ffi::Shared { z: 1011 }
    ]));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_enum(ffi::Enum::AVal));
}

#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(old_value, unique_ptr.set2(old_value));
    assert_eq!(old_value, unique_ptr.get2())
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
    Box::into_raw(Box::new(2020usize))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
    *r == 2020
}
<commit_msg>Fix for when std::os::raw::c_char is u8 (armv7)<commit_after>use cxx_test_suite::ffi;
use std::cell::Cell;
use std::ffi::CStr;

thread_local! {
    static CORRECT: Cell<bool> = Cell::new(false);
}

#[no_mangle]
extern "C" fn cxx_test_suite_set_correct() {
    CORRECT.with(|correct| correct.set(true));
}

macro_rules! check {
    ($run:expr) => {{
        CORRECT.with(|correct| correct.set(false));
        $run;
        assert!(CORRECT.with(|correct| correct.get()), stringify!($run));
    }};
}

#[test]
fn test_c_return() {
    let shared = ffi::Shared { z: 2020 };

    assert_eq!(2020, ffi::c_return_primitive());
    assert_eq!(2020, ffi::c_return_shared().z);
    assert_eq!(2020, *ffi::c_return_box());
    ffi::c_return_unique_ptr();
    assert_eq!(2020, *ffi::c_return_ref(&shared));
    assert_eq!("2020", ffi::c_return_str(&shared));
    assert_eq!(b"2020\0", ffi::c_return_sliceu8(&shared));
    assert_eq!("2020", ffi::c_return_rust_string());
    assert_eq!("2020", ffi::c_return_unique_ptr_string().to_str().unwrap());
    assert_eq!(4, ffi::c_return_unique_ptr_vector_u8().len());
    assert_eq!(
        200_u8,
        ffi::c_return_unique_ptr_vector_u8().into_iter().sum(),
    );
    assert_eq!(
        200.5_f64,
        ffi::c_return_unique_ptr_vector_f64().into_iter().sum(),
    );
    assert_eq!(2, ffi::c_return_unique_ptr_vector_shared().len());
    assert_eq!(
        2021_usize,
        ffi::c_return_unique_ptr_vector_shared()
            .into_iter()
            .map(|o| o.z)
            .sum(),
    );
    assert_eq!(2020, ffi::c_return_identity(2020));
    assert_eq!(2021, ffi::c_return_sum(2020, 1));
    match ffi::c_return_enum(0) {
        enm @ ffi::Enum::AVal => assert_eq!(0, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(1) {
        enm @ ffi::Enum::BVal => assert_eq!(2020, enm.repr),
        _ => assert!(false),
    }
    match ffi::c_return_enum(2021) {
        enm @ ffi::Enum::CVal => assert_eq!(2021, enm.repr),
        _ => assert!(false),
    }
}

#[test]
fn test_c_try_return() {
    assert_eq!((), ffi::c_try_return_void().unwrap());
    assert_eq!(2020, ffi::c_try_return_primitive().unwrap());
    assert_eq!(
        "logic error",
        ffi::c_fail_return_primitive().unwrap_err().what(),
    );
    assert_eq!(2020, *ffi::c_try_return_box().unwrap());
    assert_eq!("2020", *ffi::c_try_return_ref(&"2020".to_owned()).unwrap());
    assert_eq!("2020", ffi::c_try_return_str("2020").unwrap());
    assert_eq!(b"2020", ffi::c_try_return_sliceu8(b"2020").unwrap());
    assert_eq!("2020", ffi::c_try_return_rust_string().unwrap());
    assert_eq!("2020", &*ffi::c_try_return_unique_ptr_string().unwrap());
}

#[test]
fn test_c_take() {
    let unique_ptr = ffi::c_return_unique_ptr();

    check!(ffi::c_take_primitive(2020));
    check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
    check!(ffi::c_take_box(Box::new(2020)));
    check!(ffi::c_take_ref_c(&unique_ptr));
    check!(cxx_test_suite::module::ffi::c_take_unique_ptr(unique_ptr));
    check!(ffi::c_take_str("2020"));
    check!(ffi::c_take_sliceu8(b"2020"));
    check!(ffi::c_take_rust_string("2020".to_owned()));
    check!(ffi::c_take_unique_ptr_string(
        ffi::c_return_unique_ptr_string()
    ));
    check!(ffi::c_take_unique_ptr_vector_u8(
        ffi::c_return_unique_ptr_vector_u8()
    ));
    check!(ffi::c_take_unique_ptr_vector_f64(
        ffi::c_return_unique_ptr_vector_f64()
    ));
    check!(ffi::c_take_unique_ptr_vector_shared(
        ffi::c_return_unique_ptr_vector_shared()
    ));
    check!(ffi::c_take_ref_vector(&ffi::c_return_unique_ptr_vector_u8()));
    let test_vec = [86_u8, 75_u8, 30_u8, 9_u8].to_vec();
    check!(ffi::c_take_rust_vec(test_vec.clone()));
    check!(ffi::c_take_rust_vec_shared(vec![
        ffi::Shared { z: 1010 },
        ffi::Shared { z: 1011 }
    ]));
    check!(ffi::c_take_rust_vec_shared_forward_iterator(vec![
        ffi::Shared { z: 1010 },
        ffi::Shared { z: 1011 }
    ]));
    check!(ffi::c_take_ref_rust_vec(&test_vec));
    check!(ffi::c_take_ref_rust_vec_copy(&test_vec));
    check!(ffi::c_take_enum(ffi::Enum::AVal));
}

#[test]
fn test_c_callback() {
    fn callback(s: String) -> usize {
        if s == "2020" {
            cxx_test_suite_set_correct();
        }
        0
    }

    check!(ffi::c_take_callback(callback));
}

#[test]
fn test_c_call_r() {
    fn cxx_run_test() {
        extern "C" {
            fn cxx_run_test() -> *const i8;
        }
        let failure = unsafe { cxx_run_test() };
        if !failure.is_null() {
            let msg = unsafe { CStr::from_ptr(failure as *mut std::os::raw::c_char) };
            eprintln!("{}", msg.to_string_lossy());
        }
    }
    check!(cxx_run_test());
}

#[test]
fn test_c_method_calls() {
    let mut unique_ptr = ffi::c_return_unique_ptr();

    let old_value = unique_ptr.get();
    assert_eq!(2020, old_value);
    assert_eq!(2021, unique_ptr.set(2021));
    assert_eq!(2021, unique_ptr.get());
    assert_eq!(old_value, unique_ptr.set2(old_value));
    assert_eq!(old_value, unique_ptr.get2())
}

#[test]
fn test_enum_representations() {
    assert_eq!(0, ffi::Enum::AVal.repr);
    assert_eq!(2020, ffi::Enum::BVal.repr);
    assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
    Box::into_raw(Box::new(2020usize))
}

#[no_mangle]
unsafe extern "C" fn cxx_test_suite_r_is_correct(r: *const cxx_test_suite::R) -> bool {
    *r == 2020
}
<|endoftext|>


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

// Test range syntax.

fn foo() -> int { 42 }

pub fn main() {
    let mut count = 0;
    for i in 0u..10 {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert!(count == 45);

    let mut count = 0;
    let mut range = 0u..10;
    for i in range {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert!(count == 45);

    let mut count = 0;
    let mut rf = 3u..;
    for i in rf.take(10) {
        assert!(i >= 3 && i < 13);
        count += i;
    }
    assert!(count == 75);

    let _ = 0u..4+4-3;
    let _ = 0..foo();

    let _ = ..42u;

    // Test we can use two different types with a common supertype.
    let x = &42i;
    {
        let y = 42i;
        let _ = x..&y;
    }
}
<commit_msg>Add test case that RangeTo notation works in return statements.<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test range syntax.

fn foo() -> int { 42 }

// Test that range syntax works in return statements
fn return_range_to() -> ::std::ops::RangeTo<i32> { return ..1; }

pub fn main() {
    let mut count = 0;
    for i in 0u..10 {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert!(count == 45);

    let mut count = 0;
    let mut range = 0u..10;
    for i in range {
        assert!(i >= 0 && i < 10);
        count += i;
    }
    assert!(count == 45);

    let mut count = 0;
    let mut rf = 3u..;
    for i in rf.take(10) {
        assert!(i >= 3 && i < 13);
        count += i;
    }
    assert!(count == 75);

    let _ = 0u..4+4-3;
    let _ = 0..foo();

    let _ = ..42u;

    // Test we can use two different types with a common supertype.
    let x = &42i;
    {
        let y = 42i;
        let _ = x..&y;
    }
}
<|endoftext|>


<commit_before>//! Implements a command for sending events to Sentry.
use std::env;

use clap::{App, Arg, ArgMatches};
use failure::{err_msg, Error};
use itertools::Itertools;
use sentry::protocol::{Event, Level, LogEntry, User};
use serde_json::Value;
use username::get_user_name;

use config::Config;
use utils::event::{attach_logfile, get_sdk_info, with_sentry_client};
use utils::releases::detect_release_name;
use utils::system::QuietExit;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Send a manual event to Sentry.")
        .arg(
            Arg::with_name("level")
                .value_name("LEVEL")
                .long("level")
                .short("l")
                .help("Optional event severity/log level. [defaults to 'error']"),
        ).arg(
            Arg::with_name("release")
                .value_name("RELEASE")
                .long("release")
                .short("r")
                .help("Optional identifier of the release."),
        ).arg(
            Arg::with_name("dist")
                .value_name("DISTRIBUTION")
                .long("dist")
                .short("d")
                .help("Set the distribution."),
        ).arg(
            Arg::with_name("environment")
                .value_name("ENVIRONMENT")
                .long("env")
                .short("E")
                .help("Send with a specific environment."),
        ).arg(
            Arg::with_name("no_environ")
                .long("no-environ")
                .help("Do not send environment variables along"),
        ).arg(
            Arg::with_name("message")
                .value_name("MESSAGE")
                .long("message")
                .short("m")
                .multiple(true)
                .number_of_values(1)
                .help("The event message."),
        ).arg(
            Arg::with_name("message_args")
                .value_name("MESSAGE_ARG")
                .long("message-arg")
                .short("a")
                .multiple(true)
                .number_of_values(1)
                .help("Arguments for the event message."),
        ).arg(
            Arg::with_name("platform")
                .value_name("PLATFORM")
                .long("platform")
                .short("p")
                .help("Override the default 'other' platform specifier."),
        ).arg(
            Arg::with_name("tags")
                .value_name("KEY:VALUE")
                .long("tag")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .help("Add a tag (key:value) to the event."),
        ).arg(
            Arg::with_name("extra")
                .value_name("KEY:VALUE")
                .long("extra")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .help("Add extra information (key:value) to the event."),
        ).arg(
            Arg::with_name("user_data")
                .value_name("KEY:VALUE")
                .long("user")
                .short("u")
                .multiple(true)
                .number_of_values(1)
                .help(
                    "Add user information (key:value) to the event. \
                     [eg: id:42, username:foo]",
                ),
        ).arg(
            Arg::with_name("fingerprint")
                .value_name("FINGERPRINT")
                .long("fingerprint")
                .short("f")
                .multiple(true)
                .number_of_values(1)
                .help("Change the fingerprint of the event."),
        ).arg(
            Arg::with_name("logfile")
                .value_name("PATH")
                .long("logfile")
                .help("Send a logfile as breadcrumbs with the event (last 100 records)"),
        )
}

pub fn execute<'a>(matches: &ArgMatches<'a>) -> Result<(), Error> {
    let config = Config::get_current();
    let mut event = Event::default();

    event.sdk = Some(get_sdk_info());

    event.level = matches
        .value_of("level")
        .and_then(|l| l.parse().ok())
        .unwrap_or(Level::Error);

    if let Some(release) = matches.value_of("release") {
        event.release = Some(release.to_string().into());
    } else {
        event.release = detect_release_name().ok().map(|r| r.into());
    }

    event.dist = matches.value_of("dist").map(|x| x.to_string().into());
    event.platform = matches
        .value_of("platform")
        .unwrap_or("other")
        .to_string()
        .into();
    event.environment = matches
        .value_of("environment")
        .map(|x| x.to_string().into());

    if let Some(mut lines) = matches.values_of("message") {
        event.logentry = Some(LogEntry {
            message: lines.join("\n"),
            params: matches
                .values_of("message_args")
                .map(|args| args.map(|x| x.into()).collect())
                .unwrap_or_default(),
        });
    }

    for tag in matches.values_of("tags").unwrap_or_default() {
        let mut split = tag.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing tag key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing tag value"))?;
        event.tags.insert(key.into(), value.into());
    }

    if !matches.is_present("no-environ") {
        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );
    }

    for pair in matches.values_of("extra").unwrap_or_default() {
        let mut split = pair.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing extra key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing extra value"))?;
        event.extra.insert(key.into(), Value::String(value.into()));
    }

    if let Some(user_data) = matches.values_of("user_data") {
        let mut user = User::default();
        for pair in user_data {
            let mut split = pair.splitn(2, ':');
            let key = split.next().ok_or_else(|| err_msg("missing user key"))?;
            let value = split.next().ok_or_else(|| err_msg("missing user value"))?;

            match key {
                "id" => user.id = Some(value.into()),
                "email" => user.email = Some(value.into()),
                "ip_address" => user.ip_address = Some(value.parse()?),
                "username" => user.username = Some(value.into()),
                _ => {
                    user.other.insert(key.into(), value.into());
                }
            };
        }

        user.ip_address.get_or_insert(Default::default());
        event.user = Some(user);
    } else {
        event.user = get_user_name().ok().map(|n| User {
            username: Some(n),
            ip_address: Some(Default::default()),
            ..Default::default()
        });
    }

    if let Some(fingerprint) = matches.values_of("fingerprint") {
        event.fingerprint = fingerprint
            .map(|x| x.to_string().into())
            .collect::<Vec<_>>()
            .into();
    }

    if let Some(logfile) = matches.value_of("logfile") {
        attach_logfile(&mut event, logfile, false)?;
    }

    if let Some(id) = with_sentry_client(config.get_dsn()?, |c| c.capture_event(event, None)) {
        println!("Event sent: {}", id);
    } else {
        println_stderr!("error: could not send event");
        return Err(QuietExit(1).into());
    }

    Ok(())
}
<commit_msg>fix: Correctly parse send-event --no-environ (#391)<commit_after>//! Implements a command for sending events to Sentry.
use std::env;

use clap::{App, Arg, ArgMatches};
use failure::{err_msg, Error};
use itertools::Itertools;
use sentry::protocol::{Event, Level, LogEntry, User};
use serde_json::Value;
use username::get_user_name;

use config::Config;
use utils::event::{attach_logfile, get_sdk_info, with_sentry_client};
use utils::releases::detect_release_name;
use utils::system::QuietExit;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Send a manual event to Sentry.")
        .arg(
            Arg::with_name("level")
                .value_name("LEVEL")
                .long("level")
                .short("l")
                .help("Optional event severity/log level. [defaults to 'error']"),
        ).arg(
            Arg::with_name("release")
                .value_name("RELEASE")
                .long("release")
                .short("r")
                .help("Optional identifier of the release."),
        ).arg(
            Arg::with_name("dist")
                .value_name("DISTRIBUTION")
                .long("dist")
                .short("d")
                .help("Set the distribution."),
        ).arg(
            Arg::with_name("environment")
                .value_name("ENVIRONMENT")
                .long("env")
                .short("E")
                .help("Send with a specific environment."),
        ).arg(
            Arg::with_name("no_environ")
                .long("no-environ")
                .help("Do not send environment variables along"),
        ).arg(
            Arg::with_name("message")
                .value_name("MESSAGE")
                .long("message")
                .short("m")
                .multiple(true)
                .number_of_values(1)
                .help("The event message."),
        ).arg(
            Arg::with_name("message_args")
                .value_name("MESSAGE_ARG")
                .long("message-arg")
                .short("a")
                .multiple(true)
                .number_of_values(1)
                .help("Arguments for the event message."),
        ).arg(
            Arg::with_name("platform")
                .value_name("PLATFORM")
                .long("platform")
                .short("p")
                .help("Override the default 'other' platform specifier."),
        ).arg(
            Arg::with_name("tags")
                .value_name("KEY:VALUE")
                .long("tag")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .help("Add a tag (key:value) to the event."),
        ).arg(
            Arg::with_name("extra")
                .value_name("KEY:VALUE")
                .long("extra")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .help("Add extra information (key:value) to the event."),
        ).arg(
            Arg::with_name("user_data")
                .value_name("KEY:VALUE")
                .long("user")
                .short("u")
                .multiple(true)
                .number_of_values(1)
                .help(
                    "Add user information (key:value) to the event. \
                     [eg: id:42, username:foo]",
                ),
        ).arg(
            Arg::with_name("fingerprint")
                .value_name("FINGERPRINT")
                .long("fingerprint")
                .short("f")
                .multiple(true)
                .number_of_values(1)
                .help("Change the fingerprint of the event."),
        ).arg(
            Arg::with_name("logfile")
                .value_name("PATH")
                .long("logfile")
                .help("Send a logfile as breadcrumbs with the event (last 100 records)"),
        )
}

pub fn execute<'a>(matches: &ArgMatches<'a>) -> Result<(), Error> {
    let config = Config::get_current();
    let mut event = Event::default();

    event.sdk = Some(get_sdk_info());

    event.level = matches
        .value_of("level")
        .and_then(|l| l.parse().ok())
        .unwrap_or(Level::Error);

    if let Some(release) = matches.value_of("release") {
        event.release = Some(release.to_string().into());
    } else {
        event.release = detect_release_name().ok().map(|r| r.into());
    }

    event.dist = matches.value_of("dist").map(|x| x.to_string().into());
    event.platform = matches
        .value_of("platform")
        .unwrap_or("other")
        .to_string()
        .into();
    event.environment = matches
        .value_of("environment")
        .map(|x| x.to_string().into());

    if let Some(mut lines) = matches.values_of("message") {
        event.logentry = Some(LogEntry {
            message: lines.join("\n"),
            params: matches
                .values_of("message_args")
                .map(|args| args.map(|x| x.into()).collect())
                .unwrap_or_default(),
        });
    }

    for tag in matches.values_of("tags").unwrap_or_default() {
        let mut split = tag.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing tag key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing tag value"))?;
        event.tags.insert(key.into(), value.into());
    }

    if !matches.is_present("no_environ") {
        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );
    }

    for pair in matches.values_of("extra").unwrap_or_default() {
        let mut split = pair.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing extra key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing extra value"))?;
        event.extra.insert(key.into(), Value::String(value.into()));
    }

    if let Some(user_data) = matches.values_of("user_data") {
        let mut user = User::default();
        for pair in user_data {
            let mut split = pair.splitn(2, ':');
            let key = split.next().ok_or_else(|| err_msg("missing user key"))?;
            let value = split.next().ok_or_else(|| err_msg("missing user value"))?;

            match key {
                "id" => user.id = Some(value.into()),
                "email" => user.email = Some(value.into()),
                "ip_address" => user.ip_address = Some(value.parse()?),
                "username" => user.username = Some(value.into()),
                _ => {
                    user.other.insert(key.into(), value.into());
                }
            };
        }

        user.ip_address.get_or_insert(Default::default());
        event.user = Some(user);
    } else {
        event.user = get_user_name().ok().map(|n| User {
            username: Some(n),
            ip_address: Some(Default::default()),
            ..Default::default()
        });
    }

    if let Some(fingerprint) = matches.values_of("fingerprint") {
        event.fingerprint = fingerprint
            .map(|x| x.to_string().into())
            .collect::<Vec<_>>()
            .into();
    }

    if let Some(logfile) = matches.value_of("logfile") {
        attach_logfile(&mut event, logfile, false)?;
    }

    if let Some(id) = with_sentry_client(config.get_dsn()?, |c| c.capture_event(event, None)) {
        println!("Event sent: {}", id);
    } else {
        println_stderr!("error: could not send event");
        return Err(QuietExit(1).into());
    }

    Ok(())
}
<|endoftext|>


<commit_before>import std::ivec;
import std::uint;
import std::str;
import std::termivec;
import std::ioivec;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable uint[] lines);

type codemap = @rec(mutable filemap[] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let filemap[] files = ~[];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=~[0u]);
}

fn next_line(filemap file, uint pos) { file.lines += ~[pos]; }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = ivec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = ivec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    let option::t[@file_lines] maybe_lines = none;
    alt (sp) {
        case (some(?ssp)) {
            ss = span_to_str(ssp, cm);

            // FIXME: we're not able to look up lines read from .rc files yet.
            // maybe_lines = some(span_to_lines(ssp, cm));

        }
        case (none) { }
    }
    ioivec::stdout().write_str(ss + ": ");
    if (termivec::color_supported()) {
        termivec::fg(ioivec::stdout().get_buf_writer(), color);
    }
    ioivec::stdout().write_str(#fmt("%s:", kind));
    if (termivec::color_supported()) {
        termivec::reset(ioivec::stdout().get_buf_writer());
    }
    ioivec::stdout().write_str(#fmt(" %s\n", msg));
    alt (maybe_lines) {
        case (some(?lines)) {
            // FIXME: reading in the entire file is the worst possible way to
            //        get access to the necessary lines.
            auto rdr = ioivec::file_reader(lines.name);
            auto file = str::unsafe_from_bytes_ivec(rdr.read_whole_stream());
            auto fm = get_filemap(cm, lines.name);

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

            // If there's one line at fault we can easily point to the problem
            if (ivec::len(lines.lines) == 1u) {
                auto lo = codemap::lookup_pos(cm, option::get(sp).lo);
                auto lo = lookup_pos(cm, option::get(sp).lo);
                auto digits = 0u;
                auto num = lines.lines.(0) / 10u;

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

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

                s += "^";
                auto hi = lookup_pos(cm, option::get(sp).hi);
                if (hi.col != lo.col) {
                    // the ^ already takes up one space
                    auto width = hi.col - lo.col - 1u;
                    while (width > 0u) {
                        str::push_char(s, '~');
                        width -= 1u;
                    }
                }
                ioivec::stdout().write_str(s + "\n");
            }
        }
        case (_) {}
    }
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

type file_lines = rec(str name, uint[] lines);

fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
    auto lo = codemap::lookup_pos(cm, sp.lo);
    auto hi = codemap::lookup_pos(cm, sp.hi);
    auto lines = ~[];
    for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
        lines += ~[i];
    }
    ret @rec(name=lo.filename, lines=lines);
}

fn get_line(filemap fm, int line, &str file) -> str {
    let uint begin = fm.lines.(line) - fm.lines.(0);
    let uint end;
    if ((line as uint) + 1u >= ivec::len(fm.lines)) {
        end = str::byte_len(file);
    } else {
        end = fm.lines.(line + 1) - fm.lines.(0);
    }
    ret str::slice(file, begin, end);
}

fn get_filemap(codemap cm, str filename) -> filemap {
    for (filemap fm in cm.files) {
        if (fm.name == filename) {
            ret fm;
        }
    }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail;// ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Reenable error line printing.<commit_after>import std::ivec;
import std::uint;
import std::str;
import std::termivec;
import std::ioivec;
import std::option;
import std::option::some;
import std::option::none;

type filename = str;

/* A codemap is a thing that maps uints to file/line/column positions
 * in a crate. This to make it possible to represent the positions
 * with single-word things, rather than passing records all over the
 * compiler.
 */
type filemap = @rec(filename name, uint start_pos, mutable uint[] lines);

type codemap = @rec(mutable filemap[] files);

type loc = rec(filename filename, uint line, uint col);

fn new_codemap() -> codemap {
    let filemap[] files = ~[];
    ret @rec(mutable files=files);
}

fn new_filemap(filename filename, uint start_pos) -> filemap {
    ret @rec(name=filename, start_pos=start_pos, mutable lines=~[0u]);
}

fn next_line(filemap file, uint pos) { file.lines += ~[pos]; }

fn lookup_pos(codemap map, uint pos) -> loc {
    auto a = 0u;
    auto b = ivec::len[filemap](map.files);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (map.files.(m).start_pos > pos) { b = m; } else { a = m; }
    }
    auto f = map.files.(a);
    a = 0u;
    b = ivec::len[uint](f.lines);
    while (b - a > 1u) {
        auto m = (a + b) / 2u;
        if (f.lines.(m) > pos) { b = m; } else { a = m; }
    }
    ret rec(filename=f.name, line=a + 1u, col=pos - f.lines.(a));
}

type span = rec(uint lo, uint hi);

fn span_to_str(&span sp, &codemap cm) -> str {
    auto lo = lookup_pos(cm, sp.lo);
    auto hi = lookup_pos(cm, sp.hi);
    ret #fmt("%s:%u:%u:%u:%u", lo.filename, lo.line, lo.col, hi.line, hi.col);
}

fn emit_diagnostic(&option::t[span] sp, &str msg, &str kind, u8 color,
                   &codemap cm) {
    auto ss = "<input>:0:0:0:0";
    let option::t[@file_lines] maybe_lines = none;
    alt (sp) {
        case (some(?ssp)) {
            ss = span_to_str(ssp, cm);

            maybe_lines = some(span_to_lines(ssp, cm));

        }
        case (none) { }
    }
    ioivec::stdout().write_str(ss + ": ");
    if (termivec::color_supported()) {
        termivec::fg(ioivec::stdout().get_buf_writer(), color);
    }
    ioivec::stdout().write_str(#fmt("%s:", kind));
    if (termivec::color_supported()) {
        termivec::reset(ioivec::stdout().get_buf_writer());
    }
    ioivec::stdout().write_str(#fmt(" %s\n", msg));
    alt (maybe_lines) {
        case (some(?lines)) {
            // FIXME: reading in the entire file is the worst possible way to
            //        get access to the necessary lines.
            auto rdr = ioivec::file_reader(lines.name);
            auto file = str::unsafe_from_bytes_ivec(rdr.read_whole_stream());
            auto fm = get_filemap(cm, lines.name);

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

            // If there's one line at fault we can easily point to the problem
            if (ivec::len(lines.lines) == 1u) {
                auto lo = codemap::lookup_pos(cm, option::get(sp).lo);
                auto lo = lookup_pos(cm, option::get(sp).lo);
                auto digits = 0u;
                auto num = lines.lines.(0) / 10u;

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

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

                s += "^";
                auto hi = lookup_pos(cm, option::get(sp).hi);
                if (hi.col != lo.col) {
                    // the ^ already takes up one space
                    auto width = hi.col - lo.col - 1u;
                    while (width > 0u) {
                        str::push_char(s, '~');
                        width -= 1u;
                    }
                }
                ioivec::stdout().write_str(s + "\n");
            }
        }
        case (_) {}
    }
}

fn emit_warning(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "warning", 11u8, cm);
}
fn emit_error(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "error", 9u8, cm);
}
fn emit_note(&option::t[span] sp, &str msg, &codemap cm) {
    emit_diagnostic(sp, msg, "note", 10u8, cm);
}

type file_lines = rec(str name, uint[] lines);

fn span_to_lines(span sp, codemap::codemap cm) -> @file_lines {
    auto lo = codemap::lookup_pos(cm, sp.lo);
    auto hi = codemap::lookup_pos(cm, sp.hi);
    auto lines = ~[];
    for each (uint i in uint::range(lo.line - 1u, hi.line as uint)) {
        lines += ~[i];
    }
    ret @rec(name=lo.filename, lines=lines);
}

fn get_line(filemap fm, int line, &str file) -> str {
    let uint begin = fm.lines.(line) - fm.lines.(0);
    let uint end;
    if ((line as uint) + 1u >= ivec::len(fm.lines)) {
        end = str::byte_len(file);
    } else {
        end = fm.lines.(line + 1) - fm.lines.(0);
    }
    ret str::slice(file, begin, end);
}

fn get_filemap(codemap cm, str filename) -> filemap {
    for (filemap fm in cm.files) {
        if (fm.name == filename) {
            ret fm;
        }
    }
    //XXjdm the following triggers a mismatched type bug
    //      (or expected function, found _|_)
    fail;// ("asking for " + filename + " which we don't know about");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(plugin)] extern crate json_macros;
extern crate serialize;

use std::collections::TreeMap;
use serialize::json::{mod, ToJson};

#[test]
fn test_string_lit() {
    assert_eq!(json!("foo").as_string(), Some("foo"));
}

#[test]
fn test_num_lit() {
    assert_eq!(json!(1234).as_i64(), Some(1234));
    assert_eq!(json!(12345.).as_f64(), Some(12345.));
}

#[test]
fn test_null_lit() {
    assert!(json!(null).is_null());
}

#[test]
fn test_bool_lit() {
    assert_eq!(json!(true).as_boolean(), Some(true));
    assert_eq!(json!(false).as_boolean(), Some(false));
}

#[test]
fn test_array_lit() {
    assert_eq!(json!([]), json::Array(vec![]));

    let foobar = json::Array(vec!["foo".to_string().to_json(),
                                  "bar".to_string().to_json()]);
    assert_eq!(json!(["foo", "bar"]), foobar);

    let foobar = json::Array(vec!["foo".to_string().to_json(),
                             vec!["bar".to_string().to_json()].to_json()]);
    assert_eq!(json!(["foo", ["bar"]]), foobar);
}

#[test]
fn test_object_lit() {
    let empty = TreeMap::new();
    assert_eq!(json!({}), json::Object(empty));

    let mut foo_bar = TreeMap::new();
    foo_bar.insert("foo".to_string(), json!("bar"));
    assert_eq!(json!({"foo": "bar"}), json::Object(foo_bar));

    let mut foo_bar_baz_123 = TreeMap::new();
    foo_bar_baz_123.insert("foo".to_string(), json!("bar"));
    foo_bar_baz_123.insert("baz".to_string(), json!(123));
    assert_eq!(json!({
        "foo": "bar",
        "baz": 123
    }), json::Object(foo_bar_baz_123));

    let mut nested = TreeMap::new();
    let mut bar_baz = TreeMap::new();
    bar_baz.insert("bar".to_string(), json!("baz"));
    nested.insert("foo".to_string(), json::Object(bar_baz));
    nested.insert("quux".to_string(), json::Null);
    assert_eq!(json!({
        "foo": { "bar": "baz" },
        "quux": null,
    }), json::Object(nested));
}

#[test]
fn test_expr_insertion() {
    let hello = "hello world!";
    let json = json!({
        "message": (hello.into_string())
    });
    assert_eq!(json.find("message").and_then(|j| j.as_string()),
               Some(hello));
}
<commit_msg>Use fully-qualified variant paths in tests too<commit_after>#![feature(phase)]
#[phase(plugin)] extern crate json_macros;
extern crate serialize;

use std::collections::TreeMap;
use serialize::json::{Json, ToJson};

#[test]
fn test_string_lit() {
    assert_eq!(json!("foo").as_string(), Some("foo"));
}

#[test]
fn test_num_lit() {
    assert_eq!(json!(1234).as_i64(), Some(1234));
    assert_eq!(json!(12345.).as_f64(), Some(12345.));
}

#[test]
fn test_null_lit() {
    assert!(json!(null).is_null());
}

#[test]
fn test_bool_lit() {
    assert_eq!(json!(true).as_boolean(), Some(true));
    assert_eq!(json!(false).as_boolean(), Some(false));
}

#[test]
fn test_array_lit() {
    assert_eq!(json!([]), Json::Array(vec![]));

    let foobar = Json::Array(vec!["foo".to_string().to_json(),
                                  "bar".to_string().to_json()]);
    assert_eq!(json!(["foo", "bar"]), foobar);

    let foobar = Json::Array(vec!["foo".to_string().to_json(),
                             vec!["bar".to_string().to_json()].to_json()]);
    assert_eq!(json!(["foo", ["bar"]]), foobar);
}

#[test]
fn test_object_lit() {
    let empty = TreeMap::new();
    assert_eq!(json!({}), Json::Object(empty));

    let mut foo_bar = TreeMap::new();
    foo_bar.insert("foo".to_string(), json!("bar"));
    assert_eq!(json!({"foo": "bar"}), Json::Object(foo_bar));

    let mut foo_bar_baz_123 = TreeMap::new();
    foo_bar_baz_123.insert("foo".to_string(), json!("bar"));
    foo_bar_baz_123.insert("baz".to_string(), json!(123));
    assert_eq!(json!({
        "foo": "bar",
        "baz": 123
    }), Json::Object(foo_bar_baz_123));

    let mut nested = TreeMap::new();
    let mut bar_baz = TreeMap::new();
    bar_baz.insert("bar".to_string(), json!("baz"));
    nested.insert("foo".to_string(), Json::Object(bar_baz));
    nested.insert("quux".to_string(), Json::Null);
    assert_eq!(json!({
        "foo": { "bar": "baz" },
        "quux": null,
    }), Json::Object(nested));
}

#[test]
fn test_expr_insertion() {
    let hello = "hello world!";
    let json = json!({
        "message": (hello.into_string())
    });
    assert_eq!(json.find("message").and_then(|j| j.as_string()),
               Some(hello));
}
<|endoftext|>


<commit_before>#![feature(macro_vis_matcher)]

#[macro_use]
extern crate smart_default;

#[test]
fn test_unit() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo;

    assert!(Foo::default() == Foo);
}

#[test]
fn test_tuple() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo (
        #[default = "10"]
        i32,
        #[default = "20"]
        i32,
        // No default
        i32,
    );

    assert!(Foo::default() == Foo(10, 20, 0));
}

#[test]
fn test_struct() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo {
        #[default = "10"]
        x: i32,
        #[default = "20"]
        y: i32,
        // No default
        z: i32,
    }

    assert!(Foo::default() == Foo { x: 10, y: 20, z: 0 });
}

#[test]
fn test_enum_of_units() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar,
        #[default]
        Baz,
        #[allow(dead_code)]
        Qux,
    }

    assert!(Foo::default() == Foo::Baz);
}

#[test]
fn test_enum_of_tuples() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar(i32),
        #[default]
        Baz(#[default = "10"] i32, i32),
        #[allow(dead_code)]
        Qux(i32),
    }

    assert!(Foo::default() == Foo::Baz(10, 0));
}

#[test]
fn test_enum_of_structs() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar {
            x: i32,
        },
        #[default]
        Baz {
            #[default = "10"]
            y: i32,
            z: i32,
        },
        #[allow(dead_code)]
        Qux {
            w: i32,
        },
    }

    assert!(Foo::default() == Foo::Baz { y: 10, z: 0 });
}

#[test]
fn test_enum_mixed() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar,
        #[default]
        Baz(#[default = "10"] i32),
        #[allow(dead_code)]
        Qux {
            w: i32,
        },
    }

    assert!(Foo::default() == Foo::Baz(10));
}
<commit_msg>Remove `#[feature(macro_vis_matcher)] from tests.<commit_after>#[macro_use]
extern crate smart_default;

#[test]
fn test_unit() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo;

    assert!(Foo::default() == Foo);
}

#[test]
fn test_tuple() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo (
        #[default = "10"]
        i32,
        #[default = "20"]
        i32,
        // No default
        i32,
    );

    assert!(Foo::default() == Foo(10, 20, 0));
}

#[test]
fn test_struct() {
    #[derive(PartialEq, SmartDefault)]
    struct Foo {
        #[default = "10"]
        x: i32,
        #[default = "20"]
        y: i32,
        // No default
        z: i32,
    }

    assert!(Foo::default() == Foo { x: 10, y: 20, z: 0 });
}

#[test]
fn test_enum_of_units() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar,
        #[default]
        Baz,
        #[allow(dead_code)]
        Qux,
    }

    assert!(Foo::default() == Foo::Baz);
}

#[test]
fn test_enum_of_tuples() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar(i32),
        #[default]
        Baz(#[default = "10"] i32, i32),
        #[allow(dead_code)]
        Qux(i32),
    }

    assert!(Foo::default() == Foo::Baz(10, 0));
}

#[test]
fn test_enum_of_structs() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar {
            x: i32,
        },
        #[default]
        Baz {
            #[default = "10"]
            y: i32,
            z: i32,
        },
        #[allow(dead_code)]
        Qux {
            w: i32,
        },
    }

    assert!(Foo::default() == Foo::Baz { y: 10, z: 0 });
}

#[test]
fn test_enum_mixed() {
    #[derive(PartialEq, SmartDefault)]
    pub enum Foo {
        #[allow(dead_code)]
        Bar,
        #[default]
        Baz(#[default = "10"] i32),
        #[allow(dead_code)]
        Qux {
            w: i32,
        },
    }

    assert!(Foo::default() == Foo::Baz(10));
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::{process, env};
use std::path::Path;
use std::error::Error;
use std::fs::{self, OpenOptions};
/* Alias to avoid naming conflict for write_all!() */
use std::fmt::Write as std_write;

use url::Url;
use url_open::UrlOpen;

use serde_json::{from_str, to_string};

use util::*;
use util::{git_author, git_commit_message, format_file};
use timesheet::traits::HasHTML;

use timesheet::session::Session;
use timesheet::session::EventType;

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start            : u64,
    end              : u64,
    user             : String,
    pub show_commits : bool,
    repo             : String,
    sessions         : Vec<Session>,
}

impl Timesheet {
    // TODO: check if i can just write_files before the end of main()
    /** Initializes the .trk/timesheet.json file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            println!("Timesheet is already initialized!");
            return None;
        }

        /* File does not exist, initialize */
        let git_author_name = git_author();
        let author_name = match author_name {
            Some(name) => name,
            None => {
                match git_author_name {
                    Some(ref git_name) => git_name,
                    None => {
                        println!("Empty name not permitted.
Please run with 'trk init <name>'");
                        process::exit(0);
                    }
                }
            }
        };
        let now = get_seconds();
        let sheet = Timesheet {
            start        : now,
            end          : now + 1,
            user         : author_name.to_string(),
            show_commits : true,
            repo         : String::new(),
            sessions     : Vec::<Session>::new(),
        };
        if sheet.write_files() {
            Some(sheet)
        } else {
            None // TODO: error message?
        }
    }

    fn is_init() -> bool {
        Path::new("./.trk/timesheet.json").exists() && Timesheet::load_from_file().is_some()
    }

    pub fn new_session(&mut self, timestamp: Option<u64>) -> bool {
        let possible = match self.get_last_session_mut() {
            None => true,
            Some(session) => {
                if session.is_running() {
                    println!("Last session is still running.");
                    false
                } else {
                    true
                }
            }
        };
        if possible {
            match timestamp {
                Some(timestamp) => {
                    let is_valid_ts = match self.get_last_session() {
                        None => timestamp > self.start,
                        Some(last_session) => timestamp > last_session.end,
                    };
                    if is_valid_ts {
                        self.sessions.push(Session::new(Some(timestamp)));
                    } else {
                        println!("That timestamp is invalid.");
                        process::exit(0);
                    }
                }
                None => {
                    self.sessions.push(Session::new(None));
                }
            };
            self.write_files();
        }
        possible
    }

    pub fn end_session(&mut self, timestamp: Option<u64>) {
        match self.get_last_session_mut() {
            Some(session) => {
                // TODO: should it be possible to end a session multiple times?
                // Each time sets the end date later...
                session.update_end(); // TODO This is always problematic - rethink.
                session.finalize(timestamp);
            }
            None => println!("No session to finalize."),
        }
        self.write_files();
    }

    pub fn pause(&mut self, timestamp: Option<u64>, note: Option<String>) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, note, EventType::Pause);
            }
            None => println!("No session to pause."),
        }
        self.write_files();
    }

    pub fn resume(&mut self, timestamp: Option<u64>) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, None, EventType::Resume);
            }
            None => println!("No session to pause."),
        }
        self.write_files();
    }

    pub fn note(&mut self, timestamp: Option<u64>, note_text: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, Some(note_text), EventType::Note);
            }
            None => println!("No session to add note to."),
        }
        self.write_files();
    }

    pub fn add_commit(&mut self, hash: String) {
        let new_needed = match self.get_last_session() {
            Some(session) => !session.is_running(),
            None => true,
        };
        if new_needed {
            self.new_session(None);
            self.write_files();
        }
        match self.get_last_session_mut() {
            Some(session) => {
                let message = git_commit_message(&hash).unwrap_or(String::new());
                session.push_event(None, Some(message), EventType::Commit { hash });
            }
            None => println!("No session to add commit to."),
        }
        self.write_files();
    }

    pub fn add_branch(&mut self, name: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.add_branch(name);
            }
            None => {}
        }
        self.write_files();
    }

    fn get_last_session(&self) -> Option<&Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&self.sessions[n - 1]),
        }
    }

    fn get_last_session_mut(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn write_to_html(&self, ago: Option<u64>) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./timesheet.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                file.write_all(self.to_html(ago).as_bytes()).unwrap();
                format_file("timesheet.html");
                /* Save was successful */
                true
            }
            Err(why) => {
                println!("Could not report sheet! {}", why.description());
                false
            }
        }
    }

    pub fn write_last_session_html(&self) -> bool {
        let path = Path::new("./session.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                match self.get_last_session() {
                    Some(session) => {
                        let stylesheets = match self.show_commits {
                            true => {
                                r#"<link rel="stylesheet" type="text/css" href="style.css">
"#
                                        .to_string()
                            }
                            false => {
                                r#"<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="no_commit.css">
"#
                                        .to_string()
                            }
                        };

                        let html = format!(r#"<!DOCTYPE html>

  {}
  <title>{} for {}</title>

{}

"#,
                                           stylesheets,
                                           "Session",
                                           "Rafael Bachmann",
                                           session.to_html());
                        file.write_all(html.as_bytes()).unwrap();
                        format_file("session.html");
                        /* Save was successful */
                        true
                    }
                    /* TODO: write empty file anyway? */
                    None => true,
                }
            }
            Err(why) => {
                println!("Could not write report to session.html! {}",
                         why.description());
                false
            }
        }
    }

    fn write_to_json(&self) -> bool {
        if !Path::new("./.trk").exists() {
            match fs::create_dir("./.trk") {
                Ok(_) => {}
                _ => {
                    println!("Could not create .trk directory.");
                    process::exit(0);
                }
            }
        }

        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    to_string(&self).expect("Could not write serialized time sheet.");
                file.write_all(serialized.as_bytes()).unwrap();
                /* Save was successful */
                true
            }
            Err(why) => {
                println!("Could not open timesheet.json file: {}", why.description());
                false
            }
        }
    }

    fn write_files(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */
        self.write_to_json() && self.write_to_html(None) && self.write_last_session_html()
    }

    /** Return a Some(Timesheet) struct if a timesheet.json file
     * is present and valid in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("IO error while reading the timesheet file.");
                        process::exit(0);
                    }
                }
            }
            Err(..) => None,
        }
    }

    /* TODO: Check why timestamps aren't overwritten */
    pub fn clear() {
        /* Try to get user name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/timesheet.json");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        let mut status = String::new();
        write!(&mut status,
               "Sheet running for {}\n",
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        match self.sessions.len() {
            0 => write!(&mut status, "No sessions yet.\n").unwrap(),
            n => {
                write!(&mut status,
                       "{} session(s) so far.\nLast session:\n{}",
                       n,
                       self.sessions[n - 1].status())
                        .unwrap()
            }
        };
        status
    }

    pub fn last_session_status(&self) -> String {
        let status = self.get_last_session().map(|session| session.status());
        match status {
            None => "No session yet.".to_string(),
            Some(status) => status,
        }
    }

    pub fn report_last_session(&self) {
        // We assume that we are in a valid directory.
        let mut p = env::current_dir().unwrap();
        p.push("session.html");
        let path = p.as_path();
        let mut path_str = "file://".to_string();
        path_str.push_str(path.to_str().unwrap());
        Url::parse(&path_str).unwrap().open();
    }

    pub fn report_sheet(&self, ago: Option<u64>) {
        self.write_to_html(ago);
        // We assume that we are in a valid directory.
        let mut p = env::current_dir().unwrap();
        p.push("timesheet.html");
        let path = p.as_path();
        let mut path_str = "file://".to_string();
        path_str.push_str(path.to_str().unwrap());
        Url::parse(&path_str).unwrap().open();
        /* clean up html again */
        self.write_to_html(None);
    }

    pub fn toggle_show_git_info(&mut self, setting: bool) {
        self.show_commits = setting;
        self.write_files();
    }

    pub fn set_repo_url(&mut self, repo: String) {
        self.repo = repo;
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        for session in &self.sessions {
            pause_time += session.pause_time();
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let mut work_time = 0;
        for session in &self.sessions {
            work_time += session.working_time();
        }
        work_time
    }

    fn to_html(&self, ago: Option<u64>) -> String {
        let timestamp = match ago {
            Some(ago) => ago,
            None      => self.start,
        };
        let mut sessions_html = String::new();
        for session in &self.sessions {
            if session.start > timestamp {
		write!(&mut sessions_html, "{}
", session.to_html()).unwrap();
            }
        }

        let stylesheets = match self.show_commits {
            true => {
                r#"<link rel="stylesheet" type="text/css" href="style.css">
"#
                        .to_string()
            }
            false => {
                r#"
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="no_commit.css">
"#
                        .to_string()
            }
        };

        let mut html = format!(r#"<!DOCTYPE html>

        {}
        <title>{} for {}</title>

    {}"#,
                               stylesheets,
                               "Timesheet",
                               "Rafael Bachmann",
                               sessions_html);

        write!(&mut html,
               r#"

Worked for {}

Paused for {}

"#,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();
        write!(&mut html, "\n").unwrap();
        html
    }
}<commit_msg>greatly simplify write_last_session_html<commit_after>use std::io::prelude::*;
use std::{process, env};
use std::path::Path;
use std::error::Error;
use std::fs::{self, OpenOptions};
/* Alias to avoid naming conflict for write_all!() */
use std::fmt::Write as std_write;

use url::Url;
use url_open::UrlOpen;

use serde_json::{from_str, to_string};

use util::*;
use util::{git_author, git_commit_message, format_file};
use timesheet::traits::HasHTML;

use timesheet::session::Session;
use timesheet::session::EventType;

#[derive(Serialize, Deserialize, Debug)]
pub struct Timesheet {
    start            : u64,
    end              : u64,
    user             : String,
    pub show_commits : bool,
    repo             : String,
    sessions         : Vec<Session>,
}

impl Timesheet {
    // TODO: check if i can just write_files before the end of main()
    /** Initializes the .trk/timesheet.json file which holds
     * the serialized timesheet
     * Returns Some(newTimesheet) if operation succeeded */
    pub fn init(author_name: Option<&str>) -> Option<Timesheet> {
        /* Check if file already exists (no init permitted) */
        if Timesheet::is_init() {
            println!("Timesheet is already initialized!");
            return None;
        }

        /* File does not exist, initialize */
        let git_author_name = git_author();
        let author_name = match author_name {
            Some(name) => name,
            None => {
                match git_author_name {
                    Some(ref git_name) => git_name,
                    None => {
                        println!("Empty name not permitted.
Please run with 'trk init <name>'");
                        process::exit(0);
                    }
                }
            }
        };
        let now = get_seconds();
        let sheet = Timesheet {
            start        : now,
            end          : now + 1,
            user         : author_name.to_string(),
            show_commits : true,
            repo         : String::new(),
            sessions     : Vec::<Session>::new(),
        };
        if sheet.write_files() {
            Some(sheet)
        } else {
            None // TODO: error message?
        }
    }

    fn is_init() -> bool {
        Path::new("./.trk/timesheet.json").exists() && Timesheet::load_from_file().is_some()
    }

    pub fn new_session(&mut self, timestamp: Option<u64>) -> bool {
        let possible = match self.get_last_session_mut() {
            None => true,
            Some(session) => {
                if session.is_running() {
                    println!("Last session is still running.");
                    false
                } else {
                    true
                }
            }
        };
        if possible {
            match timestamp {
                Some(timestamp) => {
                    let is_valid_ts = match self.get_last_session() {
                        None => timestamp > self.start,
                        Some(last_session) => timestamp > last_session.end,
                    };
                    if is_valid_ts {
                        self.sessions.push(Session::new(Some(timestamp)));
                    } else {
                        println!("That timestamp is invalid.");
                        process::exit(0);
                    }
                }
                None => {
                    self.sessions.push(Session::new(None));
                }
            };
            self.write_files();
        }
        possible
    }

    pub fn end_session(&mut self, timestamp: Option<u64>) {
        match self.get_last_session_mut() {
            Some(session) => {
                // TODO: should it be possible to end a session multiple times?
                // Each time sets the end date later...
                session.update_end(); // TODO This is always problematic - rethink.
                session.finalize(timestamp);
            }
            None => println!("No session to finalize."),
        }
        self.write_files();
    }

    pub fn pause(&mut self, timestamp: Option<u64>, note: Option<String>) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, note, EventType::Pause);
            }
            None => println!("No session to pause."),
        }
        self.write_files();
    }

    pub fn resume(&mut self, timestamp: Option<u64>) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, None, EventType::Resume);
            }
            None => println!("No session to pause."),
        }
        self.write_files();
    }

    pub fn note(&mut self, timestamp: Option<u64>, note_text: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.push_event(timestamp, Some(note_text), EventType::Note);
            }
            None => println!("No session to add note to."),
        }
        self.write_files();
    }

    pub fn add_commit(&mut self, hash: String) {
        let new_needed = match self.get_last_session() {
            Some(session) => !session.is_running(),
            None => true,
        };
        if new_needed {
            self.new_session(None);
            self.write_files();
        }
        match self.get_last_session_mut() {
            Some(session) => {
                let message = git_commit_message(&hash).unwrap_or(String::new());
                session.push_event(None, Some(message), EventType::Commit { hash });
            }
            None => println!("No session to add commit to."),
        }
        self.write_files();
    }

    pub fn add_branch(&mut self, name: String) {
        match self.get_last_session_mut() {
            Some(session) => {
                session.add_branch(name);
            }
            None => {}
        }
        self.write_files();
    }

    fn get_last_session(&self) -> Option<&Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&self.sessions[n - 1]),
        }
    }

    fn get_last_session_mut(&mut self) -> Option<&mut Session> {
        match self.sessions.len() {
            0 => None,
            n => Some(&mut self.sessions[n - 1]),
        }
    }

    pub fn write_to_html(&self, ago: Option<u64>) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */

        let path = Path::new("./timesheet.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                file.write_all(self.to_html(ago).as_bytes()).unwrap();
                format_file("timesheet.html");
                /* Save was successful */
                true
            }
            Err(why) => {
                println!("Could not report sheet! {}", why.description());
                false
            }
        }
    }

    pub fn write_last_session_html(&self) -> bool {
        let path = Path::new("./session.html");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        let mut file = match file {
            Ok(file) => file,
            Err(why) => {
                println!("Could not write report to session.html! {}",
                        why.description());
                return false;
            }
        };

        let session = match self.get_last_session() {
            Some(session) => session,
            /* TODO: write empty file anyway? */
            None => return true,
        };

        let stylesheets = match self.show_commits {
            true  => r#"<link rel="stylesheet" type="text/css" href="style.css">
"#,
            false => r#"<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="no_commit.css">
"#,
        };

        let html = format!(r#"<!DOCTYPE html>

  {}
  <title>{} for {}</title>

{}

"#,
                   stylesheets,
                   "Session",
                   "Rafael Bachmann",
                   session.to_html());
    file.write_all(html.as_bytes()).unwrap();
    format_file("session.html");
    /* Save was successful */
    true
    }

    fn write_to_json(&self) -> bool {
        if !Path::new("./.trk").exists() {
            match fs::create_dir("./.trk") {
                Ok(_) => {}
                _ => {
                    println!("Could not create .trk directory.");
                    process::exit(0);
                }
            }
        }

        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new()
            .write(true)
            .truncate(true)
            .create(true)
            .open(&path);

        match file {
            Ok(mut file) => {
                /* Convert the sheet to a JSON string. */
                let serialized =
                    to_string(&self).expect("Could not write serialized time sheet.");
                file.write_all(serialized.as_bytes()).unwrap();
                /* Save was successful */
                true
            }
            Err(why) => {
                println!("Could not open timesheet.json file: {}", why.description());
                false
            }
        }
    }

    fn write_files(&self) -> bool {
        /* TODO: avoid time-of-check-to-time-of-use race risk */
        /* TODO: make all commands run regardless of where trk is executed
         * (and not just in root which is assumed here */
        self.write_to_json() && self.write_to_html(None) && self.write_last_session_html()
    }

    /** Return a Some(Timesheet) struct if a timesheet.json file
     * is present and valid in the .trk directory, and None otherwise.
     * TODO: improve error handling
     * */
    pub fn load_from_file() -> Option<Timesheet> {
        let path = Path::new("./.trk/timesheet.json");
        let file = OpenOptions::new().read(true).open(&path);
        match file {
            Ok(mut file) => {
                let mut serialized = String::new();
                match file.read_to_string(&mut serialized) {
                    Ok(..) => from_str(&serialized).unwrap_or(None),
                    Err(..) => {
                        println!("IO error while reading the timesheet file.");
                        process::exit(0);
                    }
                }
            }
            Err(..) => None,
        }
    }

    /* TODO: Check why timestamps aren't overwritten */
    pub fn clear() {
        /* Try to get user name */
        let sheet = Timesheet::load_from_file();
        let name: Option<String> = sheet.map(|s| s.user.clone());

        let path = Path::new("./.trk/timesheet.json");
        if path.exists() {
            match fs::remove_file(&path) {
                Ok(..) => {}
                Err(why) => println!("Could not remove sessions file: {}", why.description()),
            }
        }
        match name {
            Some(name) => {
                /* Overwrite file */
                Timesheet::init(Some(&name));
            }
            None => {
                Timesheet::init(None);
            }
        }
    }

    pub fn timesheet_status(&self) -> String {
        let mut status = String::new();
        write!(&mut status,
               "Sheet running for {}\n",
               sec_to_hms_string(get_seconds() - self.start))
                .unwrap();
        match self.sessions.len() {
            0 => write!(&mut status, "No sessions yet.\n").unwrap(),
            n => {
                write!(&mut status,
                       "{} session(s) so far.\nLast session:\n{}",
                       n,
                       self.sessions[n - 1].status())
                        .unwrap()
            }
        };
        status
    }

    pub fn last_session_status(&self) -> String {
        let status = self.get_last_session().map(|session| session.status());
        match status {
            None => "No session yet.".to_string(),
            Some(status) => status,
        }
    }

    pub fn report_last_session(&self) {
        // We assume that we are in a valid directory.
        let mut p = env::current_dir().unwrap();
        p.push("session.html");
        let path = p.as_path();
        let mut path_str = "file://".to_string();
        path_str.push_str(path.to_str().unwrap());
        Url::parse(&path_str).unwrap().open();
    }

    pub fn report_sheet(&self, ago: Option<u64>) {
        self.write_to_html(ago);
        // We assume that we are in a valid directory.
        let mut p = env::current_dir().unwrap();
        p.push("timesheet.html");
        let path = p.as_path();
        let mut path_str = "file://".to_string();
        path_str.push_str(path.to_str().unwrap());
        Url::parse(&path_str).unwrap().open();
        /* clean up html again */
        self.write_to_html(None);
    }

    pub fn toggle_show_git_info(&mut self, setting: bool) {
        self.show_commits = setting;
        self.write_files();
    }

    pub fn set_repo_url(&mut self, repo: String) {
        self.repo = repo;
    }

    pub fn pause_time(&self) -> u64 {
        let mut pause_time = 0;
        for session in &self.sessions {
            pause_time += session.pause_time();
        }
        pause_time
    }

    pub fn working_time(&self) -> u64 {
        let mut work_time = 0;
        for session in &self.sessions {
            work_time += session.working_time();
        }
        work_time
    }

    fn to_html(&self, ago: Option<u64>) -> String {
        let timestamp = match ago {
            Some(ago) => ago,
            None      => self.start,
        };
        let mut sessions_html = String::new();
        for session in &self.sessions {
            if session.start > timestamp {
		write!(&mut sessions_html, "{}
", session.to_html()).unwrap();
            }
        }

        let stylesheets = match self.show_commits {
            true => {
                r#"<link rel="stylesheet" type="text/css" href="style.css">
"#
                        .to_string()
            }
            false => {
                r#"
<link rel="stylesheet" type="text/css" href="style.css">
<link rel="stylesheet" type="text/css" href="no_commit.css">
"#
                        .to_string()
            }
        };

        let mut html = format!(r#"<!DOCTYPE html>

        {}
        <title>{} for {}</title>

    {}"#,
                               stylesheets,
                               "Timesheet",
                               "Rafael Bachmann",
                               sessions_html);

        write!(&mut html,
               r#"

Worked for {}

Paused for {}

"#,
               sec_to_hms_string(self.working_time()),
               sec_to_hms_string(self.pause_time()))
                .unwrap();
        write!(&mut html, "\n").unwrap();
        html
    }
}<|endoftext|>