<commit_before>use std::cmp::Ord;
use std::option::{None, Option, Some};
use std::to_string::ToString;

/// FourNode
struct FourNode<V: ToString+Ord>(V, V, V, Option<(Box<Node<V>>, Box<Node<V>>, Box<Node<V>>, Box<Node<V>>)>);

impl <V: ToString+Ord> FourNode<V> {

    fn from_leaf_3node_and_value(node: Node<V>, value: V) -> FourNode<V> {
        match node {
            LeafThreeNode(value1, value2) =>
                if value > value2      { FourNode(value1, value2, value, None) }
                else if value < value1 { FourNode(value, value1, value2, None) }
                else                   { FourNode(value1, value, value2, None) },
            _ => fail!(""),
        }
    }

    /// Convert a Split into a TwoNode with the middle value as the value of the new parent node
    fn to_two_node(self) -> Node<V> {
        match self {
            FourNode(v1, v2, v3, None) => TwoNode(v2, box LeafTwoNode(v1), box LeafTwoNode(v3)),
            FourNode(v1, v2, v3, Some((n1, n2, n3, n4))) => TwoNode(v2, box TwoNode(v1, n1, n2), box TwoNode(v3, n3, n4)),
        }
    }
}

/// InsertResult
enum InsertResult<V: ToString+Ord> {
    Fit(Node<V>),  // box this node?
    Split(FourNode<V>),  // split should always contain a ThreeNode/LeafThreeNode. box this?
}

/// Direction
enum Direction {
    Left,
    Middle,
    Right,
    Leaf,
}

/// Node
enum Node<V: ToString+Ord> {
    LeafTwoNode(V),
    LeafThreeNode(V, V),
    TwoNode(V, Box<Node<V>>, Box<Node<V>>),
    ThreeNode(V, V, Box<Node<V>>, Box<Node<V>>, Box<Node<V>>),
}

impl <V: ToString+Ord> ToString for Node<V> {
    fn to_string(&self) -> String {
        match *self {
            LeafTwoNode(ref v) => format!("LeafTwoNode({:s})", v.to_string()),
            LeafThreeNode(ref v1, ref v2) => format!("LeafThreeNode({:s}, {:s})", v1.to_string(), v2.to_string()),
            TwoNode(ref v, ref left, ref middle) => format!("TwoNode({:s}, {:s}, {:s})", v.to_string(), left.to_string(), middle.to_string()),
            ThreeNode(_, _, ref left, ref middle, ref right) => format!("ThreeNode({:s}, {:s}, {:s})", left.to_string(), middle.to_string(), right.to_string()),
        }
    }
}

impl <V: ToString+Ord> Node<V> {
    // PRIVATE
    /// Extend this TwoNode with the provided value and child node to make it a ThreeNode
    fn extend(self, other_value: V, other_node: Box<Node<V>>) -> Node<V> {
        match self {
            TwoNode(self_value, self_left, self_middle) => {
                if self_value > other_value {
                    ThreeNode(other_value, self_value, other_node, self_left, self_middle)
                } else {
                    ThreeNode(self_value, other_value, self_left, self_middle, other_node)
                }
            }
            _ => fail!(""),
        }
    }

    fn extend4(self, other_value: V, other_node: Box<Node<V>>) -> FourNode<V> {
        match self {
            ThreeNode(self_value1, self_value2, self_left, self_middle, self_right) => {
                if other_value < self_value1 {
                    FourNode(other_value, self_value1, self_value2, Some((other_node, self_left, self_middle, self_right)))
                } else {
                    FourNode(self_value1, self_value2, other_value, Some((self_left, self_middle, self_right, other_node)))
                }
            }
            _ => fail!(""),
        }
    }

    fn next_direction(&self, to_insert: &V) -> Direction {
        match self {
            &TwoNode(ref value, _, _) =>
                if to_insert < value { Left }
                else                 { Middle },
            &ThreeNode(ref value1, ref value2, _, _, _) =>
                if      to_insert < value1 { Left }
                else if to_insert > value2 { Right }
                else                       { Middle },
            _ => Leaf,
        }
    }

    fn contains_node(&self, to_check: &V) -> bool {
        match self {
            &TwoNode(ref v, _, _) |
                &LeafTwoNode(ref v) if to_check == v => true,

            &ThreeNode(ref v1, ref v2, _, _, _) |
                &LeafThreeNode(ref v1, ref v2) if to_check == v1 || to_check == v2 => true,

            _ => false
        }
    }

    fn insert(self, to_insert: V) -> InsertResult<V> {
        // If value is already in this node, we're done (prevent duplicates)
        if self.contains_node(&to_insert) { return Fit(self); }

        let next_direction = self.next_direction(&to_insert);

        match self {
            // Insert if leaf TwoNode
            LeafTwoNode(value) => {
                if to_insert > value { Fit(LeafThreeNode(value, to_insert)) }
                else                 { Fit(LeafThreeNode(to_insert, value)) }
            },

            // Split if leaf ThreeNode
            node @ LeafThreeNode(..) => {
                let four_node = FourNode::from_leaf_3node_and_value(node, to_insert);
                Split(four_node)
            },

            // Recurse down if internal Node and handle results
            TwoNode(value, box left, box middle) => {

                // Determine which node we'll recurse next
                let (next_node, other_node) = match next_direction {
                    Left => (left, middle),
                    Middle => (middle, left),
                    _ => fail!(""),
                };

                // Recurse, save the result of the insert
                let insert_result = next_node.insert(to_insert);

                // Handle the result of the insert
                let new_node = match insert_result {
                    Fit(returned_node) =>
                        match next_direction {
                            Left => TwoNode(value, box returned_node, box other_node),
                            Middle => TwoNode(value, box other_node, box returned_node),
                            _ => fail!(""),
                        },
                    Split(four_node) => four_node.to_two_node().extend(value, box other_node),
                };

                Fit(new_node)
            },

            // Recurse down if internal Node and handle results
            ThreeNode(value1, value2, left, middle, right) => {

                // Determine which node we'll recurse next
                let (next_node, other_node1, other_node2) = match next_direction {
                    Left => (left, middle, right),
                    Middle => (middle, left, right),
                    Right => (right, left, middle),
                    _ => fail!(""),
                };

                let insert_result = next_node.insert(to_insert);

                match insert_result {
                    Fit(returned_node) => {
                        let new_node = match next_direction {
                            Left => ThreeNode(value1, value2, box returned_node, other_node1, other_node2),
                            Middle => ThreeNode(value1, value2, other_node1, box returned_node, other_node2),
                            Right => ThreeNode(value1, value2, other_node1, other_node2, box returned_node),
                            _ => fail!(""),
                        };
                        Fit(new_node)
                    },
                    Split(four_node) => {
                        let new_node: FourNode<V> = match next_direction {
                            Left => four_node.to_two_node().extend(value1, other_node1).extend4(value2, other_node2),
                            Middle => four_node.to_two_node().extend(value1, other_node1).extend4(value2, other_node2),
                            Right => four_node.to_two_node().extend(value2, other_node2).extend4(value1, other_node1),
                            _ => fail!(""),
                        };
                        Split(new_node)
                    }
                }
            }
        }
    }
}

/// TTTree
pub struct TTTree<V: ToString+Ord> {
    root: Option<Node<V>>,
}

impl <V: ToString+Ord> ToString for TTTree<V> {
    fn to_string(&self) -> String {
        match self.root {
            Some(ref node) => node.to_string(),
            None => "TTTree(<empty>)".to_string(),
        }
    }
}

impl <V: ToString+Ord> TTTree<V> {
    pub fn new() -> TTTree<V> {
        TTTree { root: None }
    }

    pub fn insert(&mut self, value: V) -> () {
        let root: Option<Node<V>> = self.root.take();
        let new_root: Node<V> = match root {
            None => LeafTwoNode(value),
            Some(root) => {
                let result = root.insert(value);
                match result {
                    Fit(node) => node,
                    Split(node @ FourNode(..)) => node.to_two_node(),
                }
            }
        };
        self.root = Some(new_root);
    }
}

// TODO:
// Collection trait
// convert this to Key/Value K/V
// remove ToString debugging stuffs

#[test]
fn it_works() {
    let mut tree: TTTree<uint> = TTTree::new();

    tree.insert(50);
    match tree {
        TTTree{root: Some(LeafTwoNode(50))} => (),
        _ => fail!(),
    }

    tree.insert(50);
    match tree {
        TTTree{root: Some(LeafTwoNode(50))} => (),
        _ => fail!(),
    }

    tree.insert(60);
    match tree {
        TTTree{root: Some(LeafThreeNode(50, 60))} => (),
        _ => fail!(),
    }

    tree.insert(70);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafTwoNode(50),
                box LeafTwoNode(70)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(100);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafTwoNode(50),
                box LeafThreeNode(70, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(55);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafThreeNode(50, 55),
                box LeafThreeNode(70, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(68);
    match tree {
        TTTree{root: Some(
            ThreeNode(60, 70,
                box LeafThreeNode(50, 55),
                box LeafTwoNode(68),
                box LeafTwoNode(100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(75);
    match tree {
        TTTree{root: Some(
            ThreeNode(60, 70,
                box LeafThreeNode(50, 55),
                box LeafTwoNode(68),
                box LeafThreeNode(75, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(52);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box TwoNode(52,
                    box LeafTwoNode(50),
                    box LeafTwoNode(55),
                ),
                box TwoNode(70,
                    box LeafTwoNode(68),
                    box LeafThreeNode(75, 100),
                ),
            )
        )} => (),
        _ => fail!(),
    }
}
<commit_msg>Wrap lines in an attempt to improve readability<commit_after>use std::cmp::Ord;
use std::option::{None, Option, Some};
use std::to_string::ToString;

/// FourNode
struct FourNode<V: ToString+Ord>(V, V, V, Option<(Box<Node<V>>, Box<Node<V>>, Box<Node<V>>, Box<Node<V>>)>);

impl <V: ToString+Ord> FourNode<V> {

    fn from_leaf_3node_and_value(node: Node<V>, value: V) -> FourNode<V> {
        match node {
            LeafThreeNode(value1, value2) =>
                if value > value2      { FourNode(value1, value2, value, None) }
                else if value < value1 { FourNode(value, value1, value2, None) }
                else                   { FourNode(value1, value, value2, None) },
            _ => fail!(""),
        }
    }

    /// Convert a Split into a TwoNode with the middle value as the value of the new parent node
    fn to_two_node(self) -> Node<V> {
        match self {
            FourNode(v1, v2, v3, None) =>
                TwoNode(v2,
                    box LeafTwoNode(v1),
                    box LeafTwoNode(v3)),
            FourNode(v1, v2, v3, Some((n1, n2, n3, n4))) =>
                TwoNode(v2,
                    box TwoNode(v1, n1, n2),
                    box TwoNode(v3, n3, n4)),
        }
    }
}

/// InsertResult
enum InsertResult<V: ToString+Ord> {
    Fit(Node<V>),  // box this node?
    Split(FourNode<V>),  // split should always contain a ThreeNode/LeafThreeNode. box this?
}

/// Direction
enum Direction {
    Left,
    Middle,
    Right,
    Leaf,
}

/// Node
enum Node<V: ToString+Ord> {
    LeafTwoNode(V),
    LeafThreeNode(V, V),
    TwoNode(V, Box<Node<V>>, Box<Node<V>>),
    ThreeNode(V, V, Box<Node<V>>, Box<Node<V>>, Box<Node<V>>),
}

impl <V: ToString+Ord> ToString for Node<V> {
    fn to_string(&self) -> String {
        match *self {
            LeafTwoNode(ref v) => format!("LeafTwoNode({:s})", v.to_string()),
            LeafThreeNode(ref v1, ref v2) => format!("LeafThreeNode({:s}, {:s})", v1.to_string(), v2.to_string()),
            TwoNode(ref v, ref left, ref middle) => format!("TwoNode({:s}, {:s}, {:s})", v.to_string(), left.to_string(), middle.to_string()),
            ThreeNode(_, _, ref left, ref middle, ref right) => format!("ThreeNode({:s}, {:s}, {:s})", left.to_string(), middle.to_string(), right.to_string()),
        }
    }
}

impl <V: ToString+Ord> Node<V> {
    // PRIVATE
    /// Extend this TwoNode with the provided value and child node to make it a ThreeNode
    fn extend(self, other_value: V, other_node: Box<Node<V>>) -> Node<V> {
        match self {
            TwoNode(self_value, self_left, self_middle) => {
                if self_value > other_value {
                    ThreeNode(other_value, self_value, other_node, self_left, self_middle)
                } else {
                    ThreeNode(self_value, other_value, self_left, self_middle, other_node)
                }
            }
            _ => fail!(""),
        }
    }

    fn extend4(self, other_value: V, other_node: Box<Node<V>>) -> FourNode<V> {
        match self {
            ThreeNode(self_value1, self_value2, self_left, self_middle, self_right) => {
                if other_value < self_value1 {
                    FourNode(other_value, self_value1, self_value2, Some((other_node, self_left, self_middle, self_right)))
                } else {
                    FourNode(self_value1, self_value2, other_value, Some((self_left, self_middle, self_right, other_node)))
                }
            }
            _ => fail!(""),
        }
    }

    fn next_direction(&self, to_insert: &V) -> Direction {
        match self {
            &TwoNode(ref value, _, _) =>
                if to_insert < value { Left }
                else                 { Middle },
            &ThreeNode(ref value1, ref value2, _, _, _) =>
                if      to_insert < value1 { Left }
                else if to_insert > value2 { Right }
                else                       { Middle },
            _ => Leaf,
        }
    }

    fn contains_node(&self, to_check: &V) -> bool {
        match self {
            &TwoNode(ref v, _, _) |
                &LeafTwoNode(ref v) if to_check == v => true,

            &ThreeNode(ref v1, ref v2, _, _, _) |
                &LeafThreeNode(ref v1, ref v2) if to_check == v1 || to_check == v2 => true,

            _ => false
        }
    }

    fn insert(self, to_insert: V) -> InsertResult<V> {
        // If value is already in this node, we're done (prevent duplicates)
        if self.contains_node(&to_insert) { return Fit(self); }

        let next_direction = self.next_direction(&to_insert);

        match self {
            // Insert if leaf TwoNode
            LeafTwoNode(value) => {
                if to_insert > value { Fit(LeafThreeNode(value, to_insert)) }
                else                 { Fit(LeafThreeNode(to_insert, value)) }
            },

            // Split if leaf ThreeNode
            node @ LeafThreeNode(..) => {
                let four_node = FourNode::from_leaf_3node_and_value(node, to_insert);
                Split(four_node)
            },

            // Recurse down if internal Node and handle results
            TwoNode(value, box left, box middle) => {

                // Determine which node we'll recurse next
                let (next_node, other_node) = match next_direction {
                    Left => (left, middle),
                    Middle => (middle, left),
                    _ => fail!(""),
                };

                // Recurse, save the result of the insert
                let insert_result = next_node.insert(to_insert);

                // Handle the result of the insert
                let new_node = match insert_result {
                    Fit(returned_node) =>
                        match next_direction {
                            Left => TwoNode(value, box returned_node, box other_node),
                            Middle => TwoNode(value, box other_node, box returned_node),
                            _ => fail!(""),
                        },
                    Split(four_node) => four_node.to_two_node().extend(value, box other_node),
                };

                Fit(new_node)
            },

            // Recurse down if internal Node and handle results
            ThreeNode(value1, value2, left, middle, right) => {

                // Determine which node we'll recurse next
                let (next_node, other_node1, other_node2) = match next_direction {
                    Left => (left, middle, right),
                    Middle => (middle, left, right),
                    Right => (right, left, middle),
                    _ => fail!(""),
                };

                let insert_result = next_node.insert(to_insert);

                match insert_result {
                    Fit(returned_node) => {
                        let new_node = match next_direction {
                            Left => ThreeNode(value1, value2, box returned_node, other_node1, other_node2),
                            Middle => ThreeNode(value1, value2, other_node1, box returned_node, other_node2),
                            Right => ThreeNode(value1, value2, other_node1, other_node2, box returned_node),
                            _ => fail!(""),
                        };
                        Fit(new_node)
                    },
                    Split(four_node) => {
                        let new_node: FourNode<V> = match next_direction {
                            Left => four_node.to_two_node().extend(value1, other_node1).extend4(value2, other_node2),
                            Middle => four_node.to_two_node().extend(value1, other_node1).extend4(value2, other_node2),
                            Right => four_node.to_two_node().extend(value2, other_node2).extend4(value1, other_node1),
                            _ => fail!(""),
                        };
                        Split(new_node)
                    }
                }
            }
        }
    }
}

/// TTTree
pub struct TTTree<V: ToString+Ord> {
    root: Option<Node<V>>,
}

impl <V: ToString+Ord> ToString for TTTree<V> {
    fn to_string(&self) -> String {
        match self.root {
            Some(ref node) => node.to_string(),
            None => "TTTree(<empty>)".to_string(),
        }
    }
}

impl <V: ToString+Ord> TTTree<V> {
    pub fn new() -> TTTree<V> {
        TTTree { root: None }
    }

    pub fn insert(&mut self, value: V) -> () {
        let root: Option<Node<V>> = self.root.take();
        let new_root: Node<V> = match root {
            None => LeafTwoNode(value),
            Some(root) => {
                let result = root.insert(value);
                match result {
                    Fit(node) => node,
                    Split(node @ FourNode(..)) => node.to_two_node(),
                }
            }
        };
        self.root = Some(new_root);
    }
}

// TODO:
// Collection trait
// convert this to Key/Value K/V
// remove ToString debugging stuffs

#[test]
fn it_works() {
    let mut tree: TTTree<uint> = TTTree::new();

    tree.insert(50);
    match tree {
        TTTree{root: Some(LeafTwoNode(50))} => (),
        _ => fail!(),
    }

    tree.insert(50);
    match tree {
        TTTree{root: Some(LeafTwoNode(50))} => (),
        _ => fail!(),
    }

    tree.insert(60);
    match tree {
        TTTree{root: Some(LeafThreeNode(50, 60))} => (),
        _ => fail!(),
    }

    tree.insert(70);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafTwoNode(50),
                box LeafTwoNode(70)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(100);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafTwoNode(50),
                box LeafThreeNode(70, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(55);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box LeafThreeNode(50, 55),
                box LeafThreeNode(70, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(68);
    match tree {
        TTTree{root: Some(
            ThreeNode(60, 70,
                box LeafThreeNode(50, 55),
                box LeafTwoNode(68),
                box LeafTwoNode(100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(75);
    match tree {
        TTTree{root: Some(
            ThreeNode(60, 70,
                box LeafThreeNode(50, 55),
                box LeafTwoNode(68),
                box LeafThreeNode(75, 100)
            )
        )} => (),
        _ => fail!(),
    }

    tree.insert(52);
    match tree {
        TTTree{root: Some(
            TwoNode(60,
                box TwoNode(52,
                    box LeafTwoNode(50),
                    box LeafTwoNode(55),
                ),
                box TwoNode(70,
                    box LeafTwoNode(68),
                    box LeafThreeNode(75, 100),
                ),
            )
        )} => (),
        _ => fail!(),
    }
}
<|endoftext|>


<commit_before>extern crate nix;
extern crate cargo;
extern crate gimli;
extern crate object;
extern crate memmap;
extern crate coveralls_api;
extern crate fallible_iterator;
extern crate rustc_demangle;
extern crate syn;
extern crate proc_macro2;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate serde;
extern crate serde_json;
extern crate quick_xml;
extern crate regex;
extern crate walkdir;

use std::env;
use std::io;
use std::ffi::CString;
use std::path::Path;
use nix::unistd::*;
use cargo::util::Config as CargoConfig;
use cargo::core::{Workspace, Package};
use cargo::ops;

pub mod config;
pub mod test_loader;
pub mod breakpoint;
pub mod report;
pub mod traces;
mod statemachine;
mod source_analysis;

/// Should be unnecessary with a future nix crate release.
mod personality;
mod ptrace_control;

use config::*;
use test_loader::*;
use ptrace_control::*;
use statemachine::*;
use traces::*;

pub fn run(config: Config) -> Result<(), i32> {
    let result = launch_tarpaulin(&config)?;
    report_coverage(&config, &result);
    Ok(())
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(config: &Config) -> Result<TraceMap, i32> {
    let mut cargo_config = CargoConfig::default().unwrap();
    let flag_quiet = if config.verbose {
        None
    } else {
        Some(true)
    };
    // This shouldn't fail so no checking the error.
    let _ = cargo_config.configure(0u32, flag_quiet, &None, false, false, &[]);

    let workspace = Workspace::new(config.manifest.as_path(), &cargo_config).map_err(|_| 1i32)?;

    setup_environment();

    let mut copt = ops::CompileOptions::default(&cargo_config, ops::CompileMode::Test);
    match copt.filter {
        ops::CompileFilter::Default{ref mut required_features_filterable} => {
            *required_features_filterable = true;
        },
        _ => {},
    }
    copt.features = config.features.as_slice();
    copt.all_features = config.all_features;
    copt.spec = match ops::Packages::from_flags(workspace.is_virtual(), config.all, &config.exclude, &config.packages) {
        Ok(spec) => spec,
        Err(e) => {
            println!("Error getting Packages from workspace {}", e);
            return Err(-1)
        }
    };
    if config.verbose {
        println!("Running Tarpaulin");
    }
    if !config.skip_clean {
        if config.verbose {
            println!("Cleaning project");
        }
        // Clean isn't expected to fail and if it does it likely won't have an effect
        let clean_opt = ops::CleanOptions {
            config: &cargo_config,
            spec: &[],
            target: None,
            release: false,
        };
        let _ = ops::clean(&workspace, &clean_opt);
    }
    let mut result = TraceMap::new();
    println!("Building project");
    let compilation = ops::compile(&workspace, &copt);
    match compilation {
        Ok(comp) => {
            for &(ref package, ref _target_kind, ref name, ref path) in &comp.tests {
                if config.verbose {
                    println!("Processing {}", name);
                }
                if let Some(res) = get_test_coverage(&workspace, package, path.as_path(), &config, false) {
                    result.merge(&res);
                }
                if config.run_ignored {
                    if let Some(res) = get_test_coverage(&workspace, package, path.as_path(),
                                                         &config, true) {
                        result.merge(&res);
                    }
                }
            }
            result.dedup();
            Ok(result)
        },
        Err(e) => {
            if config.verbose{
                println!("Error: failed to compile: {}", e);
            }
            Err(-1)
        },
    }
}

fn setup_environment() {
    let rustflags = "RUSTFLAGS";
    let mut value = " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 ".to_string();
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustflags, value);
}

/// Reports the test coverage using the users preferred method. See config.rs
/// or help text for details.
pub fn report_coverage(config: &Config, result: &TraceMap) {
    if !result.is_empty() {
        println!("Coverage Results");
        if config.verbose {
            for (ref key, ref value) in result.iter() {
                let path = config.strip_project_path(key);
                for v in value.iter() {
                    println!("{}:{} - {}", path.display(), v.line, v.stats);
                }
            }
            println!("");
        }
        for file in result.files() {
            let path = config.strip_project_path(file);
            println!("{}: {}/{}", path.display(), result.covered_in_path(&file), result.coverable_in_path(&file));
        }
        let percent = result.coverage_percentage() * 100.0f64;
        // Put file filtering here
        println!("\n{:.2}% coverage, {}/{} lines covered", percent,
                 result.total_covered(), result.total_coverable());
        if config.is_coveralls() {
            report::coveralls::export(result, config);
            println!("Coverage data sent");
        }

        for g in &config.generate {
            match g {
                &OutputFile::Xml => {
                    report::cobertura::export(result, config);
                },
                _ => {
                    println!("Format currently unsupported");
                },
            }
        }
    } else {
        println!("No coverage results collected.");
    }

}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(project: &Workspace,
                         package: &Package,
                         test: &Path,
                         config: &Config,
                         ignored: bool) -> Option<TraceMap> {
    if !test.exists() {
        return None;
    }
    match fork() {
        Ok(ForkResult::Parent{ child }) => {
            match collect_coverage(project, test, child, config) {
                Ok(t) => {
                    Some(t)
                },
                Err(e) => {
                    println!("Error occurred: {}", e);
                    None
                },
            }
        }
        Ok(ForkResult::Child) => {
            println!("Launching test");
            execute_test(test, package, ignored, config);
            None
        }
        Err(err) => {
            println!("Failed to run {}", test.display());
            println!("Error {}", err);
            None
        }
    }

}

/// Collects the coverage data from the launched test
fn collect_coverage(project: &Workspace,
                    test_path: &Path,
                    test: Pid,
                    config: &Config) -> io::Result<TraceMap> {
    let mut traces = generate_tracemap(project, test_path, config)?;
    {
        let (mut state, mut data) = create_state_machine(test, &mut traces, config);
        loop {
            state = state.step(&mut data, config);
            if state.is_finished() {
                break;
            }
        }
        if let Some(m) = data.error_message {
            println!("{}", m);
        }
        if state == TestState::Abort  {
            println!("Can't collect coverage data. Exiting");
            std::process::exit(1);
        }
    }
    Ok(traces)
}

/// Launches the test executable
fn execute_test(test: &Path, package: &Package, ignored: bool, config: &Config) {
    let exec_path = CString::new(test.to_str().unwrap()).unwrap();
    match personality::disable_aslr() {
        Ok(_) => {},
        Err(e) => println!("ASLR disable failed: {}", e),
    }
    request_trace().expect("Failed to trace");
    println!("running {}", test.display());
    if let Some(parent) = package.manifest_path().parent() {
        let _ = env::set_current_dir(parent);
    }

    let mut envars: Vec<CString> = vec![CString::new("RUST_TEST_THREADS=1").unwrap()];
    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    } else {
        argv.push(CString::new("--quiet").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }
    execve(&exec_path, &argv, envars.as_slice())
        .unwrap();
}

<commit_msg>Collect uncovered line numbers<commit_after>extern crate nix;
extern crate cargo;
extern crate gimli;
extern crate object;
extern crate memmap;
extern crate coveralls_api;
extern crate fallible_iterator;
extern crate rustc_demangle;
extern crate syn;
extern crate proc_macro2;
#[macro_use]
extern crate clap;
#[macro_use]
extern crate lazy_static;
extern crate serde;
extern crate serde_json;
extern crate quick_xml;
extern crate regex;
extern crate walkdir;

use std::env;
use std::io;
use std::ffi::CString;
use std::path::Path;
use nix::unistd::*;
use cargo::util::Config as CargoConfig;
use cargo::core::{Workspace, Package};
use cargo::ops;

pub mod config;
pub mod test_loader;
pub mod breakpoint;
pub mod report;
pub mod traces;
mod statemachine;
mod source_analysis;

/// Should be unnecessary with a future nix crate release.
mod personality;
mod ptrace_control;

use config::*;
use test_loader::*;
use ptrace_control::*;
use statemachine::*;
use traces::*;

pub fn run(config: Config) -> Result<(), i32> {
    let result = launch_tarpaulin(&config)?;
    report_coverage(&config, &result);
    Ok(())
}

/// Launches tarpaulin with the given configuration.
pub fn launch_tarpaulin(config: &Config) -> Result<TraceMap, i32> {
    let mut cargo_config = CargoConfig::default().unwrap();
    let flag_quiet = if config.verbose {
        None
    } else {
        Some(true)
    };
    // This shouldn't fail so no checking the error.
    let _ = cargo_config.configure(0u32, flag_quiet, &None, false, false, &[]);

    let workspace = Workspace::new(config.manifest.as_path(), &cargo_config).map_err(|_| 1i32)?;

    setup_environment();

    let mut copt = ops::CompileOptions::default(&cargo_config, ops::CompileMode::Test);
    match copt.filter {
        ops::CompileFilter::Default{ref mut required_features_filterable} => {
            *required_features_filterable = true;
        },
        _ => {},
    }
    copt.features = config.features.as_slice();
    copt.all_features = config.all_features;
    copt.spec = match ops::Packages::from_flags(workspace.is_virtual(), config.all, &config.exclude, &config.packages) {
        Ok(spec) => spec,
        Err(e) => {
            println!("Error getting Packages from workspace {}", e);
            return Err(-1)
        }
    };
    if config.verbose {
        println!("Running Tarpaulin");
    }
    if !config.skip_clean {
        if config.verbose {
            println!("Cleaning project");
        }
        // Clean isn't expected to fail and if it does it likely won't have an effect
        let clean_opt = ops::CleanOptions {
            config: &cargo_config,
            spec: &[],
            target: None,
            release: false,
        };
        let _ = ops::clean(&workspace, &clean_opt);
    }
    let mut result = TraceMap::new();
    println!("Building project");
    let compilation = ops::compile(&workspace, &copt);
    match compilation {
        Ok(comp) => {
            for &(ref package, ref _target_kind, ref name, ref path) in &comp.tests {
                if config.verbose {
                    println!("Processing {}", name);
                }
                if let Some(res) = get_test_coverage(&workspace, package, path.as_path(), &config, false) {
                    result.merge(&res);
                }
                if config.run_ignored {
                    if let Some(res) = get_test_coverage(&workspace, package, path.as_path(),
                                                         &config, true) {
                        result.merge(&res);
                    }
                }
            }
            result.dedup();
            Ok(result)
        },
        Err(e) => {
            if config.verbose{
                println!("Error: failed to compile: {}", e);
            }
            Err(-1)
        },
    }
}

fn setup_environment() {
    let rustflags = "RUSTFLAGS";
    let mut value = " -C relocation-model=dynamic-no-pic -C link-dead-code -C opt-level=0 ".to_string();
    if let Ok(vtemp) = env::var(rustflags) {
        value.push_str(vtemp.as_ref());
    }
    env::set_var(rustflags, value);
}

/// Reports the test coverage using the users preferred method. See config.rs
/// or help text for details.
pub fn report_coverage(config: &Config, result: &TraceMap) {
    if !result.is_empty() {
        println!("Coverage Results");
        if config.verbose {
            for (ref key, ref value) in result.iter() {
                let path = config.strip_project_path(key);
                let mut uncovered_lines = vec![];
                for v in value.iter() {
                    match v.stats {
                        traces::CoverageStat::Line(count) if count == 0 => {
                            uncovered_lines.push(v.line);
                        },
                        _ => (),
                    }
                    // println!("{}:{} - {}", path.display(), v.line, v.stats);
                }
                println!("{}: {:?}", path.display(), uncovered_lines);
            }
            println!("");
        }
        for file in result.files() {
            let path = config.strip_project_path(file);
            println!("{}: {}/{}", path.display(), result.covered_in_path(&file), result.coverable_in_path(&file));
        }
        let percent = result.coverage_percentage() * 100.0f64;
        // Put file filtering here
        println!("\n{:.2}% coverage, {}/{} lines covered", percent,
                 result.total_covered(), result.total_coverable());
        if config.is_coveralls() {
            report::coveralls::export(result, config);
            println!("Coverage data sent");
        }

        for g in &config.generate {
            match g {
                &OutputFile::Xml => {
                    report::cobertura::export(result, config);
                },
                _ => {
                    println!("Format currently unsupported");
                },
            }
        }
    } else {
        println!("No coverage results collected.");
    }

}

/// Returns the coverage statistics for a test executable in the given workspace
pub fn get_test_coverage(project: &Workspace,
                         package: &Package,
                         test: &Path,
                         config: &Config,
                         ignored: bool) -> Option<TraceMap> {
    if !test.exists() {
        return None;
    }
    match fork() {
        Ok(ForkResult::Parent{ child }) => {
            match collect_coverage(project, test, child, config) {
                Ok(t) => {
                    Some(t)
                },
                Err(e) => {
                    println!("Error occurred: {}", e);
                    None
                },
            }
        }
        Ok(ForkResult::Child) => {
            println!("Launching test");
            execute_test(test, package, ignored, config);
            None
        }
        Err(err) => {
            println!("Failed to run {}", test.display());
            println!("Error {}", err);
            None
        }
    }

}

/// Collects the coverage data from the launched test
fn collect_coverage(project: &Workspace,
                    test_path: &Path,
                    test: Pid,
                    config: &Config) -> io::Result<TraceMap> {
    let mut traces = generate_tracemap(project, test_path, config)?;
    {
        let (mut state, mut data) = create_state_machine(test, &mut traces, config);
        loop {
            state = state.step(&mut data, config);
            if state.is_finished() {
                break;
            }
        }
        if let Some(m) = data.error_message {
            println!("{}", m);
        }
        if state == TestState::Abort  {
            println!("Can't collect coverage data. Exiting");
            std::process::exit(1);
        }
    }
    Ok(traces)
}

/// Launches the test executable
fn execute_test(test: &Path, package: &Package, ignored: bool, config: &Config) {
    let exec_path = CString::new(test.to_str().unwrap()).unwrap();
    match personality::disable_aslr() {
        Ok(_) => {},
        Err(e) => println!("ASLR disable failed: {}", e),
    }
    request_trace().expect("Failed to trace");
    println!("running {}", test.display());
    if let Some(parent) = package.manifest_path().parent() {
        let _ = env::set_current_dir(parent);
    }

    let mut envars: Vec<CString> = vec![CString::new("RUST_TEST_THREADS=1").unwrap()];
    for (key, value) in env::vars() {
        let mut temp = String::new();
        temp.push_str(key.as_str());
        temp.push('=');
        temp.push_str(value.as_str());
        envars.push(CString::new(temp).unwrap());
    }
    let mut argv = if ignored {
        vec![exec_path.clone(), CString::new("--ignored").unwrap()]
    } else {
        vec![exec_path.clone()]
    };
    if config.verbose {
        envars.push(CString::new("RUST_BACKTRACE=1").unwrap());
    } else {
        argv.push(CString::new("--quiet").unwrap());
    }
    for s in &config.varargs {
        argv.push(CString::new(s.as_bytes()).unwrap_or_default());
    }
    execve(&exec_path, &argv, envars.as_slice())
        .unwrap();
}

<|endoftext|>


<commit_before>// a Shiny test framework
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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.

#![crate_name = "shiny"]
#![crate_type = "dylib"]
#![feature(plugin_registrar)]

extern crate rustc;
extern crate syntax;

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ptr::P;
use syntax::ast;
use syntax::ast_util::empty_generics;
use syntax::codemap::DUMMY_SP;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::tts_to_parser;
use syntax::util::small_vector::SmallVector;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("describe", macro_describe);
}

pub fn macro_describe(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
    let sess = cx.parse_sess();
    let ttsvec = tts.iter().map(|x| (*x).clone()).collect();

    let mut parser = tts_to_parser(sess, ttsvec, cx.cfg());

    let mut before_block = None;
    let mut test_blocks = vec!();

    loop {
        if parser.token == token::Eof {
            break;
        }

        let ident = parser.parse_ident();
        match ident.as_str() {
            "before_each" => {
                if before_block.is_some() {
                    panic!("only one before_each block is allowed");
                }
                before_block = Some(parser.parse_block());
            },
            "it" => {
                let (name, _) = parser.parse_str();
                let block = parser.parse_block();
                test_blocks.push((name.get().to_string(), block));
            }
            other => {
                let span = parser.span;
                parser.span_fatal(span, format!("expected one of: {} but found `{}`",
                    "`before_each`, `it`",
                    other).as_slice());
            },
        }
    }

    let mut funcs = vec!();

    for &(ref name, ref block) in test_blocks.iter() {
        let body = match before_block {
            None => block.clone(),
            Some(ref before) => {
                P(ast::Block {
                    view_items: before.view_items + block.view_items,
                    stmts: before.stmts + block.stmts,

                    ..block.deref().clone()
                })
            }
        };

        let attr_test = cx.attribute(DUMMY_SP,
            cx.meta_word(DUMMY_SP, token::InternedString::new("test")));

        let func = P(ast::Item {
            ident: cx.ident_of(name.replace(" ", "_").as_slice()),
            attrs: vec!(attr_test),
            id: ast::DUMMY_NODE_ID,
            node: ast::ItemFn(
                cx.fn_decl(Vec::new(), cx.ty_nil()),
                ast::NormalFn,
                abi::Rust,
                empty_generics(),
                body),
            vis: ast::Inherited,
            span: DUMMY_SP,
        });
        funcs.push(func);
    }

    MacItems::new(funcs)
}

pub struct MacItems {
    items: Vec<P<ast::Item>>
}

impl MacItems {
    pub fn new(items: Vec<P<ast::Item>>) -> Box<MacResult+'static> {
        box MacItems { items: items } as Box<MacResult>
    }
}

impl MacResult for MacItems {
    fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
        Some(SmallVector::many(self.items.clone()))
    }
}
<commit_msg>ExtCtxt::ty_nil no longer exists<commit_after>// a Shiny test framework
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// 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.

#![crate_name = "shiny"]
#![crate_type = "dylib"]
#![feature(plugin_registrar)]

extern crate rustc;
extern crate syntax;

use rustc::plugin::Registry;
use syntax::abi;
use syntax::ptr::P;
use syntax::ast;
use syntax::ast_util::empty_generics;
use syntax::codemap::DUMMY_SP;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::parse::tts_to_parser;
use syntax::util::small_vector::SmallVector;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("describe", macro_describe);
}

pub fn macro_describe(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
    let sess = cx.parse_sess();
    let ttsvec = tts.iter().map(|x| (*x).clone()).collect();

    let mut parser = tts_to_parser(sess, ttsvec, cx.cfg());

    let mut before_block = None;
    let mut test_blocks = vec!();

    loop {
        if parser.token == token::Eof {
            break;
        }

        let ident = parser.parse_ident();
        match ident.as_str() {
            "before_each" => {
                if before_block.is_some() {
                    panic!("only one before_each block is allowed");
                }
                before_block = Some(parser.parse_block());
            },
            "it" => {
                let (name, _) = parser.parse_str();
                let block = parser.parse_block();
                test_blocks.push((name.get().to_string(), block));
            }
            other => {
                let span = parser.span;
                parser.span_fatal(span, format!("expected one of: {} but found `{}`",
                    "`before_each`, `it`",
                    other).as_slice());
            },
        }
    }

    let mut funcs = vec!();

    for &(ref name, ref block) in test_blocks.iter() {
        let body = match before_block {
            None => block.clone(),
            Some(ref before) => {
                P(ast::Block {
                    view_items: before.view_items + block.view_items,
                    stmts: before.stmts + block.stmts,

                    ..block.deref().clone()
                })
            }
        };

        let attr_test = cx.attribute(DUMMY_SP,
            cx.meta_word(DUMMY_SP, token::InternedString::new("test")));

        let func = P(ast::Item {
            ident: cx.ident_of(name.replace(" ", "_").as_slice()),
            attrs: vec!(attr_test),
            id: ast::DUMMY_NODE_ID,
            node: ast::ItemFn(
                cx.fn_decl(Vec::new(), cx.ty(DUMMY_SP, ast::Ty_::TyTup(Vec::new()))),
                ast::NormalFn,
                abi::Rust,
                empty_generics(),
                body),
            vis: ast::Inherited,
            span: DUMMY_SP,
        });
        funcs.push(func);
    }

    MacItems::new(funcs)
}

pub struct MacItems {
    items: Vec<P<ast::Item>>
}

impl MacItems {
    pub fn new(items: Vec<P<ast::Item>>) -> Box<MacResult+'static> {
        box MacItems { items: items } as Box<MacResult>
    }
}

impl MacResult for MacItems {
    fn make_items(self: Box<MacItems>) -> Option<SmallVector<P<ast::Item>>> {
        Some(SmallVector::many(self.items.clone()))
    }
}
<|endoftext|>


<commit_before>//! The library provides [special functions][1].
//!
//! [1]: https://en.wikipedia.org/wiki/Special_functions

#![feature(macro_rules)]

extern crate libc;

pub use beta::{log_beta, inc_beta, inv_inc_beta};

mod beta;
mod m;

/// Computes the error function.
pub fn erf(x: f64) -> f64 {
    unsafe { m::erf(x) }
}

/// Computes the complementary error function.
pub fn erfc(x: f64) -> f64 {
    unsafe { m::erfc(x) }
}

/// Computes the exponential function.
pub fn exp(x: f64) -> f64 {
    unsafe { m::exp(x) }
}

/// Computes the natural logarithm of the gamma function.
pub fn log_gamma(x: f64) -> f64 {
    unsafe {
        let mut sign: i32 = 0;
        m::lgamma_r(x, &mut sign)
    }
}

/// Computes the natural logarithm.
pub fn log(x: f64) -> f64 {
    unsafe { m::log(x) }
}

/// Computes `x` raised to the exponent `y`.
pub fn pow(x: f64, y: f64) -> f64 {
    unsafe { m::pow(x, y) }
}

/// Computes the positive square root of the argument.
pub fn sqrt(x: f64) -> f64 {
    unsafe { m::sqrt(x) }
}

#[cfg(test)]
mod tests {
    macro_rules! assert_close(
        ($x:expr, $y:expr) => ({
            let eps: f64 = ::std::f64::EPSILON.sqrt();
            let x: Vec<f64> = $x;
            let y: Vec<f64> = $y;
            for i in range(0u, x.len()) {
                assert!(::std::num::abs(x[i] - y[i]) < eps,
                       "expected {:e} ~ {:e}", x[i], y[i]);
            }
        });
    )

    macro_rules! apply(
        ($x:expr, $func:expr) => (
            $x.iter().map(|&x| $func(x)).collect()
        );
    )

    #[test]
    fn erf() {
        let x = vec![-3.0, -1.0, 1.0, 3.0];
        let y = vec![-0.9999779095030014, -0.8427007929497149,
                     0.8427007929497149, 0.9999779095030014];
        assert_close!(apply!(x, super::erf), y);
    }

    #[test]
    fn erfc() {
        let x = vec![-3.0, -1.0, 1.0, 3.0];
        let y = vec![1.9999779095030015, 1.8427007929497150,
                     0.1572992070502851, 0.0000220904969986];
        assert_close!(apply!(x, super::erfc), y);
    }

    #[test]
    fn exp() {
        let x = vec![-2.0, -1.0, 1.0, 2.0];
        let y = vec![0.1353352832366127, 0.3678794411714423,
                     2.7182818284590455, 7.3890560989306504];
        assert_close!(apply!(x, super::exp), y);
    }

    #[test]
    fn log_gamma() {
        let x = vec![0.25, 0.50, 0.75, 1.00];
        let y = vec![1.2880225246980774, 0.5723649429247000,
                     0.2032809514312953, 0.0000000000000000];
        assert_close!(apply!(x, super::log_gamma), y);
    }

    #[test]
    fn log() {
        let x = vec![0.5, 1.0, 1.5, 2.0];
        let y = vec![-0.6931471805599453, 0.0000000000000000,
                     0.4054651081081644, 0.6931471805599453];
        assert_close!(apply!(x, super::log), y);
    }
}
<commit_msg>Removed testing of libm<commit_after>//! The library provides [special functions][1].
//!
//! [1]: https://en.wikipedia.org/wiki/Special_functions

#![feature(macro_rules)]

extern crate libc;

pub use beta::{log_beta, inc_beta, inv_inc_beta};

mod beta;
mod m;

/// Computes the error function.
pub fn erf(x: f64) -> f64 {
    unsafe { m::erf(x) }
}

/// Computes the complementary error function.
pub fn erfc(x: f64) -> f64 {
    unsafe { m::erfc(x) }
}

/// Computes the exponential function.
pub fn exp(x: f64) -> f64 {
    unsafe { m::exp(x) }
}

/// Computes the natural logarithm of the gamma function.
pub fn log_gamma(x: f64) -> f64 {
    unsafe {
        let mut sign: i32 = 0;
        m::lgamma_r(x, &mut sign)
    }
}

/// Computes the natural logarithm.
pub fn log(x: f64) -> f64 {
    unsafe { m::log(x) }
}

/// Computes `x` raised to the exponent `y`.
pub fn pow(x: f64, y: f64) -> f64 {
    unsafe { m::pow(x, y) }
}

/// Computes the positive square root of the argument.
pub fn sqrt(x: f64) -> f64 {
    unsafe { m::sqrt(x) }
}
<|endoftext|>


<commit_before>//! This crate allows you to specify an offset for reads and writes, without changing the current
//! position in a file. This is similar to [`pread()` and `pwrite()`][pread] in C.
//!
//! The major advantages of this type of I/O are:
//!
//! You don't need to seek before doing a random-access read or write, which is convenient.
//! Reads don't modify the file at all, so don't require mutability.
//!
//! [pread]: http://man7.org/linux/man-pages/man2/pread.2.html
//!
//!
//! # Examples
//!
//! Read the fifth 512-byte sector of a file:
//!
//! ```no_run
//! # use std::io;
//! # use std::fs::File;
//! use positioned_io::ReadAt;
//!
//! # fn foo() -> io::Result<()> {
//! // Note that file does not need to be mut!
//! let file = try!(File::open("foo.data"));
//! let mut buf = vec![0; 512];
//! let bytes_read = try!(file.read_at(2048, &mut buf));
//! # Ok(())
//! # }
//! ```
//!
//! Write an integer to the middle of a file:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! use positioned_io::WriteAt;
//! use byteorder::{ByteOrder, LittleEndian};
//!
//! # fn foo() -> io::Result<()> {
//! // Put the integer in a buffer.
//! let mut buf = vec![0; 4];
//! LittleEndian::write_u32(&mut buf, 1234);
//!
//! // Write it to the file.
//! let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
//! try!(file.write_all_at(1 << 20, &buf));
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Or, more simply:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! # use byteorder::LittleEndian;
//! // Extend files with writing integers at offsets.
//! use positioned_io::WriteBytesExt;
//!
//! # fn foo() -> io::Result<()> {
//! let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
//! try!(file.write_u32_at::<LittleEndian>(1 << 20, 1234));
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Read from anything else that supports ReadAt, like a byte array:
//!
//! ```rust
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use byteorder::BigEndian;
//! use positioned_io::ReadBytesExt;
//!
//! # fn foo() -> io::Result<()> {
//! let buf = [0, 5, 254, 212, 0, 3];
//! let n = try!(buf.as_ref().read_i16_at::<BigEndian>(2));
//! assert_eq!(n, -300);
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```

mod byteorder;
pub use byteorder::{ReadBytesExt, WriteBytesExt};
pub use byteorder::byteio::ByteIo;

mod cursor;
pub use cursor::{Cursor, SizeCursor};

mod slice;
pub use slice::Slice;

extern crate byteorder as extbyteorder;

use std::io::{Error, ErrorKind, Result};
use std::fs::File;

/// Trait for reading at an offset.
///
/// Implementations should be able to read bytes without changing any sort of read-position.
/// Self should not change at all. Buffering reads is unlikely to be useful, since each time
/// `read_at()` is called, the position may be completely different.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```no_run
/// # use std::io;
/// # use std::fs::File;
/// use positioned_io::ReadAt;
///
/// # fn foo() -> io::Result<()> {
/// let file = try!(File::open("foo.data"));
/// let mut buf = vec![0; 512];
/// let bytes_read = try!(file.read_at(2048, &mut buf));
/// # Ok(())
/// # }
/// ```
pub trait ReadAt {
    /// Read bytes from an offset in this source into a buffer, returning how many bytes were read.
    ///
    /// This function may yield fewer bytes than the size of `buf`, if it was interrupted or hit
    /// end-of-file.
    ///
    /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read).
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize>;

    /// Read the exact number of bytes required to fill `buf`, from an offset.
    ///
    /// If only a lesser number of bytes can be read, will yield an error.
    fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.read_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        if !buf.is_empty() {
            Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
        } else {
            Ok(())
        }
    }
}

/// Trait for writing at an offset.
///
/// Implementations should be able to write bytes at an offset, without changing any sort of
/// write-position. Self should not change at all.
///
/// # Examples
///
/// ```no_run
/// # extern crate positioned_io;
/// # extern crate byteorder;
/// # use std::io;
/// # use std::fs::OpenOptions;
/// use positioned_io::WriteAt;
/// use byteorder::{ByteOrder, LittleEndian};
///
/// # fn foo() -> io::Result<()> {
/// // Put the integer in a buffer.
/// let mut buf = vec![0; 4];
/// LittleEndian::write_u32(&mut buf, 1234);
///
/// // Write it to the file.
/// let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
/// try!(file.write_all_at(1 << 20, &buf));
/// # Ok(())
/// # }
/// # fn main() { foo().unwrap() }
/// ```
pub trait WriteAt {
    /// Write a buffer at an offset, returning the number of bytes written.
    ///
    /// This function may write fewer bytes than the size of `buf`, for example if it is
    /// interrupted.
    ///
    /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write).
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> Result<usize>;

    /// Write a complete buffer at an offset.
    ///
    /// If only a lesser number of bytes can be written, will yield an error.
    fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.write_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this writer, ensuring that any buffered data is written.
    ///
    /// This should rarely do anything, since buffering is not very useful for positioned writes.
    fn flush(&mut self) -> Result<()>;
}

/// Trait to get the size of an I/O object.
///
/// Implementing this for a ReadAt or WriteAt makes it easier for users to predict whether they
/// will read past end-of-file. However, it may not be possible to implement for certain readers
/// or writers that have unknown size.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// # use std::fs::File;
/// use positioned_io::Size;
///
/// # fn foo() -> io::Result<()> {
/// let file = try!(File::open("foo.txt"));
/// let size = try!(file.size());
/// assert_eq!(size, Some(22));
///
/// // Special files probably don't have a size.
/// let file = try!(File::open("/dev/stdin"));
/// let size = try!(file.size());
/// assert_eq!(size, None);
/// # Ok(())
/// # }
/// ```
pub trait Size {
    /// Get the size of this object, in bytes.
    ///
    /// This function may return Ok(None) if the size is unknown, for example if a file is a pipe.
    ///
    /// If a positive value is returned, it should be the value such that reading at greater
    /// offsets always yields end-of-file.
    fn size(&self) -> Result<Option<u64>>;
}

impl Size for File {
    fn size(&self) -> Result<Option<u64>> {
        let md = try!(self.metadata());
        if md.is_file() {
            Ok(Some(md.len()))
        } else {
            Ok(None)
        }
    }
}

// Implementation for Unix files.
#[cfg(unix)]
mod unix;

// Implementation for Windows files.
#[cfg(windows)]
mod windows;

// Implementation for arrays, vectors.
mod array;
mod vec;
mod refs;

#[cfg(test)]
mod test;
<commit_msg>doc fix<commit_after>//! This crate allows you to specify an offset for reads and writes, without changing the current
//! position in a file. This is similar to [`pread()` and `pwrite()`][pread] in C.
//!
//! The major advantages of this type of I/O are:
//!
//! * You don't need to seek before doing a random-access read or write, which is convenient.
//! * Reads don't modify the file at all, so don't require mutability.
//!
//! [pread]: http://man7.org/linux/man-pages/man2/pread.2.html
//!
//!
//! # Examples
//!
//! Read the fifth 512-byte sector of a file:
//!
//! ```no_run
//! # use std::io;
//! # use std::fs::File;
//! use positioned_io::ReadAt;
//!
//! # fn foo() -> io::Result<()> {
//! // Note that file does not need to be mut!
//! let file = try!(File::open("foo.data"));
//! let mut buf = vec![0; 512];
//! let bytes_read = try!(file.read_at(2048, &mut buf));
//! # Ok(())
//! # }
//! ```
//!
//! Write an integer to the middle of a file:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! use positioned_io::WriteAt;
//! use byteorder::{ByteOrder, LittleEndian};
//!
//! # fn foo() -> io::Result<()> {
//! // Put the integer in a buffer.
//! let mut buf = vec![0; 4];
//! LittleEndian::write_u32(&mut buf, 1234);
//!
//! // Write it to the file.
//! let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
//! try!(file.write_all_at(1 << 20, &buf));
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Or, more simply:
//!
//! ```no_run
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use std::fs::OpenOptions;
//! # use byteorder::LittleEndian;
//! // Extend files with writing integers at offsets.
//! use positioned_io::WriteBytesExt;
//!
//! # fn foo() -> io::Result<()> {
//! let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
//! try!(file.write_u32_at::<LittleEndian>(1 << 20, 1234));
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```
//!
//! Read from anything else that supports ReadAt, like a byte array:
//!
//! ```rust
//! # extern crate positioned_io;
//! # extern crate byteorder;
//! # use std::io;
//! # use byteorder::BigEndian;
//! use positioned_io::ReadBytesExt;
//!
//! # fn foo() -> io::Result<()> {
//! let buf = [0, 5, 254, 212, 0, 3];
//! let n = try!(buf.as_ref().read_i16_at::<BigEndian>(2));
//! assert_eq!(n, -300);
//! # Ok(())
//! # }
//! # fn main() { foo().unwrap() }
//! ```

mod byteorder;
pub use byteorder::{ReadBytesExt, WriteBytesExt};
pub use byteorder::byteio::ByteIo;

mod cursor;
pub use cursor::{Cursor, SizeCursor};

mod slice;
pub use slice::Slice;

extern crate byteorder as extbyteorder;

use std::io::{Error, ErrorKind, Result};
use std::fs::File;

/// Trait for reading at an offset.
///
/// Implementations should be able to read bytes without changing any sort of read-position.
/// Self should not change at all. Buffering reads is unlikely to be useful, since each time
/// `read_at()` is called, the position may be completely different.
///
/// # Examples
///
/// Read the fifth 512-byte sector of a file:
///
/// ```no_run
/// # use std::io;
/// # use std::fs::File;
/// use positioned_io::ReadAt;
///
/// # fn foo() -> io::Result<()> {
/// let file = try!(File::open("foo.data"));
/// let mut buf = vec![0; 512];
/// let bytes_read = try!(file.read_at(2048, &mut buf));
/// # Ok(())
/// # }
/// ```
pub trait ReadAt {
    /// Read bytes from an offset in this source into a buffer, returning how many bytes were read.
    ///
    /// This function may yield fewer bytes than the size of `buf`, if it was interrupted or hit
    /// end-of-file.
    ///
    /// See [`Read::read()`](https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read).
    fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize>;

    /// Read the exact number of bytes required to fill `buf`, from an offset.
    ///
    /// If only a lesser number of bytes can be read, will yield an error.
    fn read_exact_at(&self, mut pos: u64, mut buf: &mut [u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.read_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &mut tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        if !buf.is_empty() {
            Err(Error::new(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
        } else {
            Ok(())
        }
    }
}

/// Trait for writing at an offset.
///
/// Implementations should be able to write bytes at an offset, without changing any sort of
/// write-position. Self should not change at all.
///
/// # Examples
///
/// ```no_run
/// # extern crate positioned_io;
/// # extern crate byteorder;
/// # use std::io;
/// # use std::fs::OpenOptions;
/// use positioned_io::WriteAt;
/// use byteorder::{ByteOrder, LittleEndian};
///
/// # fn foo() -> io::Result<()> {
/// // Put the integer in a buffer.
/// let mut buf = vec![0; 4];
/// LittleEndian::write_u32(&mut buf, 1234);
///
/// // Write it to the file.
/// let mut file = try!(OpenOptions::new().write(true).open("foo.data"));
/// try!(file.write_all_at(1 << 20, &buf));
/// # Ok(())
/// # }
/// # fn main() { foo().unwrap() }
/// ```
pub trait WriteAt {
    /// Write a buffer at an offset, returning the number of bytes written.
    ///
    /// This function may write fewer bytes than the size of `buf`, for example if it is
    /// interrupted.
    ///
    /// See [`Write::write()`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write).
    fn write_at(&mut self, pos: u64, buf: &[u8]) -> Result<usize>;

    /// Write a complete buffer at an offset.
    ///
    /// If only a lesser number of bytes can be written, will yield an error.
    fn write_all_at(&mut self, mut pos: u64, mut buf: &[u8]) -> Result<()> {
        while !buf.is_empty() {
            match self.write_at(pos, buf) {
                Ok(0) => break,
                Ok(n) => {
                    let tmp = buf;
                    buf = &tmp[n..];
                    pos += n as u64;
                }
                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
                Err(e) => return Err(e),
            }
        }
        Ok(())
    }

    /// Flush this writer, ensuring that any buffered data is written.
    ///
    /// This should rarely do anything, since buffering is not very useful for positioned writes.
    fn flush(&mut self) -> Result<()>;
}

/// Trait to get the size of an I/O object.
///
/// Implementing this for a ReadAt or WriteAt makes it easier for users to predict whether they
/// will read past end-of-file. However, it may not be possible to implement for certain readers
/// or writers that have unknown size.
///
/// # Examples
///
/// ```no_run
/// # use std::io;
/// # use std::fs::File;
/// use positioned_io::Size;
///
/// # fn foo() -> io::Result<()> {
/// let file = try!(File::open("foo.txt"));
/// let size = try!(file.size());
/// assert_eq!(size, Some(22));
///
/// // Special files probably don't have a size.
/// let file = try!(File::open("/dev/stdin"));
/// let size = try!(file.size());
/// assert_eq!(size, None);
/// # Ok(())
/// # }
/// ```
pub trait Size {
    /// Get the size of this object, in bytes.
    ///
    /// This function may return Ok(None) if the size is unknown, for example if a file is a pipe.
    ///
    /// If a positive value is returned, it should be the value such that reading at greater
    /// offsets always yields end-of-file.
    fn size(&self) -> Result<Option<u64>>;
}

impl Size for File {
    fn size(&self) -> Result<Option<u64>> {
        let md = try!(self.metadata());
        if md.is_file() {
            Ok(Some(md.len()))
        } else {
            Ok(None)
        }
    }
}

// Implementation for Unix files.
#[cfg(unix)]
mod unix;

// Implementation for Windows files.
#[cfg(windows)]
mod windows;

// Implementation for arrays, vectors.
mod array;
mod vec;
mod refs;

#[cfg(test)]
mod test;
<|endoftext|>


<commit_before>//! bosun_emitter - A library to emit metric data to [Bosun](http://bosun.org) from your command line.
//!
//! > "[Bosun](http://bosun.org) is an open-source, MIT licensed, monitoring and alerting system by [Stack Exchange](http://stackexchange.com/). It has an expressive domain specific language for evaluating alerts and creating detailed notifications. It also lets you test your alerts against history for a faster development experience." [[1]](http://bosun.org)
//!
//! Bosun receives metric data mostly via [scollector](http://bosun.org/scollector/) which is Boson's agent running on each monitored host. scollector runs build-in as well as external collectors periodically to collect and transmit metrics on its hosts.
//!
//! While it is easy to create external collectors suitable for most needs, there are cases in which sending a single, individual metric datum may be helpful. Such cases may comprise any individually run program such as a Cron job for backups or in general any other shell script. Further, it might be helpful to send metric data from your own application.
//!
//! **bosun_emitter** is a library that makes it easy to send metric data and corresponding meta data describing the metric. Further, this crate contains a CLI standalone binary called `emit_bosun` that may be used on the command line or from any shell script to send a single metric datum.
//!
//! # Example
//!
//! ## Library
//!
//! ```no_run
//! use bosun_emitter::{BosunClient, Datum, EmitterError, Metadata, now_in_ms, Tags};
//!
//! let client = BosunClient::new("localhost:8070");
//! let metric = "lukas.tests.count";
//!
//! let metadata = Metadata::new(&metric, "counter", "Test", "Amount of Lukas Tests");
//! let _ = client.emit_metadata(&metadata);
//!
//! let tags: Tags = Tags::new();
//! let datum = Datum::new(&metric, now_in_ms(), "42", &tags);
//!
//! match client.emit_datum(&datum) {
//!     Ok(_) => {}
//!     Err(EmitterError::JsonParseError(e)) => panic!("Failed to create JSON document."),
//!     Err(EmitterError::EmitError(e)) => panic!("Failed to send."),
//!     Err(EmitterError::ReceiveError(e)) => panic!("Failed to create resource."),
//! }
//! ```
//!
//! ## CLI Tool -- Shell Script
//!
//! ```bash
//! #!/bin/bash
//!
//! local start=$(date +%s)
//! # Complex, time consuming backup ...
//! local now=$(date +%s)
//! local runtime=$((${now}-${start}))
//!
//! emit_bosun --host localhost:8070 --hostname backup-server --tags 'type=mongodb,database=production' --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup runtime"
//! ```
//!
//! `emit_bosun` parses scollector config files for settings like Bosun server `Host, --host`, local hostname `Hostname, --hostname`, and tags `Tags, --tags`. In case scollector is configured on your host, you can omit these CLI parameters and just pass the configuration file.
//!
//! ## CLI Tool -- Parsing scollector Configuration File
//!
//! ```bash
//! > emit_bosun -c /etc/bosun/scollector.conf --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup Runtime in sec"
//! ```
//!
//! The above example scollector configuration file is the default path where `emit_bosun` looks for a configuration file. So you can even omit that parameter. In addition, tags passed on the command line will be merged with tags read from scollector's configuration file.
//!
//! ```bash
//! > emit_bosun --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup Runtime in sec"
//! ```

#![deny(missing_docs)]

extern crate chrono;
#[macro_use]
extern crate log;
extern crate rustc_serialize;
extern crate toml;

use chrono::Timelike;
use rustc_serialize::Decodable;
use rustc_serialize::json;
use rustc_serialize::json::EncoderError;
use std::collections::HashMap;
use std::convert::From;
use std::fs::File;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::path::Path;

/// Result of an attempt to send meta data or a metric datum
pub type EmitterResult = Result<(), EmitterError>;

/// Errors which may occur while sending either meta data or metric data.
#[derive(Debug)]
pub enum EmitterError {
    /// Failed to create JSON.
    JsonParseError(EncoderError),
    /// Failed to send JSON.
    EmitError(std::io::Error),
    /// Failed to create Datum on server.
    ReceiveError(String),
}

impl From<std::io::Error> for EmitterError {
    fn from(err: std::io::Error) -> EmitterError {
        EmitterError::EmitError(err)
    }
}

impl From<EncoderError> for EmitterError {
    fn from(err: EncoderError) -> EmitterError {
        EmitterError::JsonParseError(err)
    }
}

/// Encapsulates Bosun server connection.
#[derive(Debug)]
pub struct BosunClient {
    /// `<HOSTNAME|IP ADDR>:<PORT>`
    pub host: String,
}

impl BosunClient {
    /// Creates a new BosunClient.
    pub fn new(host: &str) -> BosunClient {
        BosunClient { host: host.to_string() }
    }

    /// Sends metric meta data to Bosun server.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use bosun_emitter::{BosunClient, Metadata};
    /// let metadata = Metadata::new("lukas.tests.count", "counter", "Tests", "Amount of Lukas Tests");
    ///
    /// let client = BosunClient::new("localhost:8070");
    /// let _ = client.emit_metadata(&metadata);
    /// ```
    pub fn emit_metadata(&self, metadata: &Metadata) -> EmitterResult {
        let encoded = try!(metadata.to_json());
        let res = BosunClient::send_to_bosun_api(&self.host, "/api/metadata/put", &encoded);
        info!("Sent medata '{:?}' to '{:?}' with result: '{:?}'.",
              encoded,
              self.host,
              res);

        res
    }

    /// Sends metric datum to Bosun server.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use bosun_emitter::{BosunClient, Datum, Tags};
    /// let mut tags = Tags::new();
    /// tags.insert("host".to_string(), "test-vm".to_string());
    /// let datum = Datum::new("lukas.tests.count", 1458066838, "1", &tags);
    ///
    /// let client = BosunClient::new("localhost:8070");
    /// let _ = client.emit_datum(&datum);
    /// ```
    pub fn emit_datum(&self, datum: &Datum) -> EmitterResult {
        let encoded = try!(datum.to_json());
        let res = BosunClient::send_to_bosun_api(&self.host, "/api/put", &encoded);
        info!("Sent datum '{:?}' to '{:?}' with result: '{:?}'.",
              encoded,
              &self.host,
              res);

        res
    }

    fn send_to_bosun_api(host: &str, path: &str, json: &str) -> EmitterResult {
        let mut stream = try!(TcpStream::connect(host));
        let content = json.as_bytes();
        let content_length = content.len();
        let data_str = format!("POST {} HTTP/1.1\nContent-Length: {}\nHost: {}\nContent-Type: application/json; charset=utf-8\n\n{}", path, content_length, host, json);
        let data = data_str.as_bytes();

        let tx_res = try!(stream.write(&data));
        if tx_res < data.len() {
            let underlying = std::io::Error::new(std::io::ErrorKind::Interrupted, "Failed to send request.");
            return Err(EmitterError::EmitError(underlying))
        }

        let mut result_data = [0; 1024];
        let rx_len = try!(stream.read(&mut result_data));
        let result = String::from_utf8_lossy(&result_data[0..rx_len]).to_string();
        debug!("send_to_bosun_api: Request to '{}/{}', Result: '{:?}'.",
               host,
               path,
               result);

        let splitter: Vec<&str> = result.split_whitespace().collect();
        if splitter.len() < 3 {
            let underlying = std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid response");
            return Err(EmitterError::EmitError(underlying))
        }
        match splitter[1] {
            "204" => Ok(()),
            status_code => Err(EmitterError::ReceiveError(status_code.to_string())),
        }
    }
}

#[derive(RustcEncodable, Debug)]
/// Represents metric meta data.
pub struct Metadata<'a> {
    /// Metric name
    pub metric: &'a str,
    /// Metric rate type: [gauge, counter rate]
    pub rate: &'a str,
    /// Metric unit
    pub unit: &'a str,
    /// Metric description
    pub description: &'a str,
}

// TODO: Add check for rate type: [gauge, counter rate]
impl<'a> Metadata<'a> {
    /// Creates new metric meta data.
    pub fn new(metric: &'a str,
               rate: &'a str,
               unit: &'a str,
               description: &'a str)
               -> Metadata<'a> {
        Metadata {
            metric: metric,
            rate: rate,
            unit: unit,
            description: description,
        }
    }

    /// Encodes Metadata to JSON as String.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate bosun_emitter;
    /// # extern crate rustc_serialize;
    /// # use bosun_emitter::Metadata;
    /// # use rustc_serialize::json::{Json};
    /// #
    /// # fn main() {
    /// let metadata = Metadata::new("lukas.tests.count", "counter", "Tests", "Amount of Lukas Tests");
    /// let json = metadata.to_json().unwrap();
    ///
    /// # let json_json = Json::from_str(&json).unwrap();
    /// # let expected = "[{\"value\":\"Tests\",\"name\":\"unit\",\"metric\":\"lukas.tests.count\"},{\"metric\":\"lukas.tests.count\",\"value\":\"counter\",\"name\":\"rate\"},{\"name\":\"desc\",\"metric\":\"lukas.tests.count\",\"value\":\"Amount of Lukas Tests\"}]";
    /// # let expected_json = Json::from_str(&expected).unwrap();
    /// # assert_eq!(expected_json, json_json);
    /// # }
    /// ```
    pub fn to_json(&self) -> Result<String, EmitterError> {
        let mut metadata = [HashMap::new(), HashMap::new(), HashMap::new()];
        metadata[0].insert("metric", self.metric);
        metadata[0].insert("name", "unit");
        metadata[0].insert("value", self.unit);
        metadata[1].insert("metric", self.metric);
        metadata[1].insert("name", "rate");
        metadata[1].insert("value", self.rate);
        metadata[2].insert("metric", self.metric);
        metadata[2].insert("name", "desc");
        metadata[2].insert("value", self.description);

        let json = try!(json::encode(&metadata));
        debug!("Metadata::to_json '{:?}', '{:?}'", &self, json);

        Ok(json)
    }
}

/// Metric tags equivalent to Rust's `HashMap<String, String>`
pub type Tags = HashMap<String, String>;

/// Represents a metric datum.
#[derive(RustcEncodable, Debug)]
pub struct Datum<'a> {
    /// Metric name
    pub metric: &'a str,
    /// Unix timestamp in either _s_ or _ms_
    pub timestamp: i64,
    /// Value as string representation
    pub value: &'a str,
    /// Tags for this metric datum
    pub tags: &'a Tags,
}

impl<'a> Datum<'a> {
    /// Creates a new metric datum with a specified timestamp in ms.
    pub fn new(metric: &'a str,
               timestamp: i64,
               value: &'a str,
               // TODO: make me use refs
               tags: &'a Tags)
               -> Datum<'a> {
        Datum {
            metric: metric,
            timestamp: timestamp,
            value: value,
            tags: tags,
        }
    }
    /// Creates a new metric datum with timestamp _now_.
    pub fn now(metric: &'a str,
               value: &'a str,
               // TODO: make me use refs
               tags: &'a Tags)
               -> Datum<'a> {
        Datum {
            metric: metric,
            timestamp: now_in_ms(),
            value: value,
            tags: tags,
        }
    }

    /// Encodes a Datum to JSON as String.
    ///
    /// # Example
    ///
    /// ```
    /// # use bosun_emitter::{Datum, Tags};
    /// let mut tags = Tags::new();
    /// tags.insert("host".to_string(), "test-vm".to_string());
    /// let datum = Datum::new("lukas.tests.count", 1458066838, "1", &tags);
    /// let json = datum.to_json().unwrap();
    ///
    /// # let expected = "{\"metric\":\"lukas.tests.count\",\"timestamp\":1458066838,\"value\":\"1\",\"tags\":{\"host\":\"test-vm\"}}";
    /// # assert_eq!(expected, json);
    /// ```
    pub fn to_json(&self) -> Result<String, EmitterError> {
        let json = try!(json::encode(self));
        debug!("Datum::to_json '{:?}', '{:?}'", &self, json);

        Ok(json)
    }
}

/// Returns Unix timestamp in ms.
pub fn now_in_ms() -> i64 {
    let now = chrono::Local::now();
    println!("{}.{}", now.timestamp(), now.nanosecond());
    now.timestamp() * 1000 + (now.nanosecond() / 1_000_000) as i64
}

#[derive(Debug)]
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
/// Represents connection parameters to reach Bosun as well as default tags to append to each metric
/// datum.
pub struct BosunConfig {
    /// Bosun server host name
    pub Host: String,
    /// Local host name
    pub Hostname: String,
    /// Tags to always append to each metric
    pub Tags: Tags,
}

impl BosunConfig {
    /// Creates a default configuration for `localhost`, port `8070`.
    pub fn default() -> BosunConfig {
        BosunConfig {
            Host: "localhost:8070".to_string(),
            Hostname: "localhost".to_string(),
            Tags: Tags::new(),
        }
    }

    /// Loads a configuration from an [SCollector](http://bosun.org/scollector/) configuration file.
    pub fn load_from_scollector_config(file_path: &Path) -> Result<BosunConfig, Box<std::error::Error>> {
        match BosunConfig::load_toml(file_path) {
            Ok(toml) => {
                let mut decoder = toml::Decoder::new(toml);
                let config = try!(BosunConfig::decode(&mut decoder));

                Ok(config)
            }
            Err(err) => Err(err),
        }
    }

    fn load_toml(file_path: &Path) -> Result<toml::Value, Box<std::error::Error>> {
        let mut config_file = try!(File::open(file_path));
        let mut config_content = String::new();
        try!(config_file.read_to_string(&mut config_content));

        let mut parser = toml::Parser::new(&config_content);
        match parser.parse() {
            Some(toml) => Ok(toml::Value::Table(toml)),
            None => Err(From::from(parser.errors.pop().unwrap())),
        }
    }
}

<commit_msg>Remove obsolet debug print<commit_after>//! bosun_emitter - A library to emit metric data to [Bosun](http://bosun.org) from your command line.
//!
//! > "[Bosun](http://bosun.org) is an open-source, MIT licensed, monitoring and alerting system by [Stack Exchange](http://stackexchange.com/). It has an expressive domain specific language for evaluating alerts and creating detailed notifications. It also lets you test your alerts against history for a faster development experience." [[1]](http://bosun.org)
//!
//! Bosun receives metric data mostly via [scollector](http://bosun.org/scollector/) which is Boson's agent running on each monitored host. scollector runs build-in as well as external collectors periodically to collect and transmit metrics on its hosts.
//!
//! While it is easy to create external collectors suitable for most needs, there are cases in which sending a single, individual metric datum may be helpful. Such cases may comprise any individually run program such as a Cron job for backups or in general any other shell script. Further, it might be helpful to send metric data from your own application.
//!
//! **bosun_emitter** is a library that makes it easy to send metric data and corresponding meta data describing the metric. Further, this crate contains a CLI standalone binary called `emit_bosun` that may be used on the command line or from any shell script to send a single metric datum.
//!
//! # Example
//!
//! ## Library
//!
//! ```no_run
//! use bosun_emitter::{BosunClient, Datum, EmitterError, Metadata, now_in_ms, Tags};
//!
//! let client = BosunClient::new("localhost:8070");
//! let metric = "lukas.tests.count";
//!
//! let metadata = Metadata::new(&metric, "counter", "Test", "Amount of Lukas Tests");
//! let _ = client.emit_metadata(&metadata);
//!
//! let tags: Tags = Tags::new();
//! let datum = Datum::new(&metric, now_in_ms(), "42", &tags);
//!
//! match client.emit_datum(&datum) {
//!     Ok(_) => {}
//!     Err(EmitterError::JsonParseError(e)) => panic!("Failed to create JSON document."),
//!     Err(EmitterError::EmitError(e)) => panic!("Failed to send."),
//!     Err(EmitterError::ReceiveError(e)) => panic!("Failed to create resource."),
//! }
//! ```
//!
//! ## CLI Tool -- Shell Script
//!
//! ```bash
//! #!/bin/bash
//!
//! local start=$(date +%s)
//! # Complex, time consuming backup ...
//! local now=$(date +%s)
//! local runtime=$((${now}-${start}))
//!
//! emit_bosun --host localhost:8070 --hostname backup-server --tags 'type=mongodb,database=production' --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup runtime"
//! ```
//!
//! `emit_bosun` parses scollector config files for settings like Bosun server `Host, --host`, local hostname `Hostname, --hostname`, and tags `Tags, --tags`. In case scollector is configured on your host, you can omit these CLI parameters and just pass the configuration file.
//!
//! ## CLI Tool -- Parsing scollector Configuration File
//!
//! ```bash
//! > emit_bosun -c /etc/bosun/scollector.conf --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup Runtime in sec"
//! ```
//!
//! The above example scollector configuration file is the default path where `emit_bosun` looks for a configuration file. So you can even omit that parameter. In addition, tags passed on the command line will be merged with tags read from scollector's configuration file.
//!
//! ```bash
//! > emit_bosun --metric backup.runtime --value $runtime --rate gauge --unit sec -d "Backup Runtime in sec"
//! ```

#![deny(missing_docs)]

extern crate chrono;
#[macro_use]
extern crate log;
extern crate rustc_serialize;
extern crate toml;

use chrono::Timelike;
use rustc_serialize::Decodable;
use rustc_serialize::json;
use rustc_serialize::json::EncoderError;
use std::collections::HashMap;
use std::convert::From;
use std::fs::File;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::path::Path;

/// Result of an attempt to send meta data or a metric datum
pub type EmitterResult = Result<(), EmitterError>;

/// Errors which may occur while sending either meta data or metric data.
#[derive(Debug)]
pub enum EmitterError {
    /// Failed to create JSON.
    JsonParseError(EncoderError),
    /// Failed to send JSON.
    EmitError(std::io::Error),
    /// Failed to create Datum on server.
    ReceiveError(String),
}

impl From<std::io::Error> for EmitterError {
    fn from(err: std::io::Error) -> EmitterError {
        EmitterError::EmitError(err)
    }
}

impl From<EncoderError> for EmitterError {
    fn from(err: EncoderError) -> EmitterError {
        EmitterError::JsonParseError(err)
    }
}

/// Encapsulates Bosun server connection.
#[derive(Debug)]
pub struct BosunClient {
    /// `<HOSTNAME|IP ADDR>:<PORT>`
    pub host: String,
}

impl BosunClient {
    /// Creates a new BosunClient.
    pub fn new(host: &str) -> BosunClient {
        BosunClient { host: host.to_string() }
    }

    /// Sends metric meta data to Bosun server.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use bosun_emitter::{BosunClient, Metadata};
    /// let metadata = Metadata::new("lukas.tests.count", "counter", "Tests", "Amount of Lukas Tests");
    ///
    /// let client = BosunClient::new("localhost:8070");
    /// let _ = client.emit_metadata(&metadata);
    /// ```
    pub fn emit_metadata(&self, metadata: &Metadata) -> EmitterResult {
        let encoded = try!(metadata.to_json());
        let res = BosunClient::send_to_bosun_api(&self.host, "/api/metadata/put", &encoded);
        info!("Sent medata '{:?}' to '{:?}' with result: '{:?}'.",
              encoded,
              self.host,
              res);

        res
    }

    /// Sends metric datum to Bosun server.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use bosun_emitter::{BosunClient, Datum, Tags};
    /// let mut tags = Tags::new();
    /// tags.insert("host".to_string(), "test-vm".to_string());
    /// let datum = Datum::new("lukas.tests.count", 1458066838, "1", &tags);
    ///
    /// let client = BosunClient::new("localhost:8070");
    /// let _ = client.emit_datum(&datum);
    /// ```
    pub fn emit_datum(&self, datum: &Datum) -> EmitterResult {
        let encoded = try!(datum.to_json());
        let res = BosunClient::send_to_bosun_api(&self.host, "/api/put", &encoded);
        info!("Sent datum '{:?}' to '{:?}' with result: '{:?}'.",
              encoded,
              &self.host,
              res);

        res
    }

    fn send_to_bosun_api(host: &str, path: &str, json: &str) -> EmitterResult {
        let mut stream = try!(TcpStream::connect(host));
        let content = json.as_bytes();
        let content_length = content.len();
        let data_str = format!("POST {} HTTP/1.1\nContent-Length: {}\nHost: {}\nContent-Type: application/json; charset=utf-8\n\n{}", path, content_length, host, json);
        let data = data_str.as_bytes();

        let tx_res = try!(stream.write(&data));
        if tx_res < data.len() {
            let underlying = std::io::Error::new(std::io::ErrorKind::Interrupted, "Failed to send request.");
            return Err(EmitterError::EmitError(underlying))
        }

        let mut result_data = [0; 1024];
        let rx_len = try!(stream.read(&mut result_data));
        let result = String::from_utf8_lossy(&result_data[0..rx_len]).to_string();
        debug!("send_to_bosun_api: Request to '{}/{}', Result: '{:?}'.",
               host,
               path,
               result);

        let splitter: Vec<&str> = result.split_whitespace().collect();
        if splitter.len() < 3 {
            let underlying = std::io::Error::new(std::io::ErrorKind::InvalidData, "Invalid response");
            return Err(EmitterError::EmitError(underlying))
        }
        match splitter[1] {
            "204" => Ok(()),
            status_code => Err(EmitterError::ReceiveError(status_code.to_string())),
        }
    }
}

#[derive(RustcEncodable, Debug)]
/// Represents metric meta data.
pub struct Metadata<'a> {
    /// Metric name
    pub metric: &'a str,
    /// Metric rate type: [gauge, counter rate]
    pub rate: &'a str,
    /// Metric unit
    pub unit: &'a str,
    /// Metric description
    pub description: &'a str,
}

// TODO: Add check for rate type: [gauge, counter rate]
impl<'a> Metadata<'a> {
    /// Creates new metric meta data.
    pub fn new(metric: &'a str,
               rate: &'a str,
               unit: &'a str,
               description: &'a str)
               -> Metadata<'a> {
        Metadata {
            metric: metric,
            rate: rate,
            unit: unit,
            description: description,
        }
    }

    /// Encodes Metadata to JSON as String.
    ///
    /// # Example
    ///
    /// ```
    /// # extern crate bosun_emitter;
    /// # extern crate rustc_serialize;
    /// # use bosun_emitter::Metadata;
    /// # use rustc_serialize::json::{Json};
    /// #
    /// # fn main() {
    /// let metadata = Metadata::new("lukas.tests.count", "counter", "Tests", "Amount of Lukas Tests");
    /// let json = metadata.to_json().unwrap();
    ///
    /// # let json_json = Json::from_str(&json).unwrap();
    /// # let expected = "[{\"value\":\"Tests\",\"name\":\"unit\",\"metric\":\"lukas.tests.count\"},{\"metric\":\"lukas.tests.count\",\"value\":\"counter\",\"name\":\"rate\"},{\"name\":\"desc\",\"metric\":\"lukas.tests.count\",\"value\":\"Amount of Lukas Tests\"}]";
    /// # let expected_json = Json::from_str(&expected).unwrap();
    /// # assert_eq!(expected_json, json_json);
    /// # }
    /// ```
    pub fn to_json(&self) -> Result<String, EmitterError> {
        let mut metadata = [HashMap::new(), HashMap::new(), HashMap::new()];
        metadata[0].insert("metric", self.metric);
        metadata[0].insert("name", "unit");
        metadata[0].insert("value", self.unit);
        metadata[1].insert("metric", self.metric);
        metadata[1].insert("name", "rate");
        metadata[1].insert("value", self.rate);
        metadata[2].insert("metric", self.metric);
        metadata[2].insert("name", "desc");
        metadata[2].insert("value", self.description);

        let json = try!(json::encode(&metadata));
        debug!("Metadata::to_json '{:?}', '{:?}'", &self, json);

        Ok(json)
    }
}

/// Metric tags equivalent to Rust's `HashMap<String, String>`
pub type Tags = HashMap<String, String>;

/// Represents a metric datum.
#[derive(RustcEncodable, Debug)]
pub struct Datum<'a> {
    /// Metric name
    pub metric: &'a str,
    /// Unix timestamp in either _s_ or _ms_
    pub timestamp: i64,
    /// Value as string representation
    pub value: &'a str,
    /// Tags for this metric datum
    pub tags: &'a Tags,
}

impl<'a> Datum<'a> {
    /// Creates a new metric datum with a specified timestamp in ms.
    pub fn new(metric: &'a str,
               timestamp: i64,
               value: &'a str,
               // TODO: make me use refs
               tags: &'a Tags)
               -> Datum<'a> {
        Datum {
            metric: metric,
            timestamp: timestamp,
            value: value,
            tags: tags,
        }
    }
    /// Creates a new metric datum with timestamp _now_.
    pub fn now(metric: &'a str,
               value: &'a str,
               // TODO: make me use refs
               tags: &'a Tags)
               -> Datum<'a> {
        Datum {
            metric: metric,
            timestamp: now_in_ms(),
            value: value,
            tags: tags,
        }
    }

    /// Encodes a Datum to JSON as String.
    ///
    /// # Example
    ///
    /// ```
    /// # use bosun_emitter::{Datum, Tags};
    /// let mut tags = Tags::new();
    /// tags.insert("host".to_string(), "test-vm".to_string());
    /// let datum = Datum::new("lukas.tests.count", 1458066838, "1", &tags);
    /// let json = datum.to_json().unwrap();
    ///
    /// # let expected = "{\"metric\":\"lukas.tests.count\",\"timestamp\":1458066838,\"value\":\"1\",\"tags\":{\"host\":\"test-vm\"}}";
    /// # assert_eq!(expected, json);
    /// ```
    pub fn to_json(&self) -> Result<String, EmitterError> {
        let json = try!(json::encode(self));
        debug!("Datum::to_json '{:?}', '{:?}'", &self, json);

        Ok(json)
    }
}

/// Returns Unix timestamp in ms.
pub fn now_in_ms() -> i64 {
    let now = chrono::Local::now();
    now.timestamp() * 1000 + (now.nanosecond() / 1_000_000) as i64
}

#[derive(Debug)]
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
/// Represents connection parameters to reach Bosun as well as default tags to append to each metric
/// datum.
pub struct BosunConfig {
    /// Bosun server host name
    pub Host: String,
    /// Local host name
    pub Hostname: String,
    /// Tags to always append to each metric
    pub Tags: Tags,
}

impl BosunConfig {
    /// Creates a default configuration for `localhost`, port `8070`.
    pub fn default() -> BosunConfig {
        BosunConfig {
            Host: "localhost:8070".to_string(),
            Hostname: "localhost".to_string(),
            Tags: Tags::new(),
        }
    }

    /// Loads a configuration from an [SCollector](http://bosun.org/scollector/) configuration file.
    pub fn load_from_scollector_config(file_path: &Path) -> Result<BosunConfig, Box<std::error::Error>> {
        match BosunConfig::load_toml(file_path) {
            Ok(toml) => {
                let mut decoder = toml::Decoder::new(toml);
                let config = try!(BosunConfig::decode(&mut decoder));

                Ok(config)
            }
            Err(err) => Err(err),
        }
    }

    fn load_toml(file_path: &Path) -> Result<toml::Value, Box<std::error::Error>> {
        let mut config_file = try!(File::open(file_path));
        let mut config_content = String::new();
        try!(config_file.read_to_string(&mut config_content));

        let mut parser = toml::Parser::new(&config_content);
        match parser.parse() {
            Some(toml) => Ok(toml::Value::Table(toml)),
            None => Err(From::from(parser.errors.pop().unwrap())),
        }
    }
}

<|endoftext|>