heartbeat 0.0.1

A high-performance heartbeat client.
Documentation
// Copyright 2015 Signal Analytics Ltd.

#![crate_name = "heartbeat"]
#![crate_type = "lib"]

extern crate toml;

use std::net::{SocketAddr,TcpStream};
use std::path::Path;
use std::fs;
use std::thread::sleep_ms;
use std::error;
use std::fmt;
use LoadError::{FileNotFound, IncorrectPermissions,BadConfigFile};

pub type PulseResult<'a> = Result<&'a SocketAddr, &'a  SocketAddr>;

pub type LoadResult<F> = Result<Heartbeat<F>, LoadError>;

pub type Client = Heartbeat<Fn(&SocketAddr) -> ()>;

#[derive(Debug)]
pub enum LoadError {
    FileNotFound,
    IncorrectPermissions,
    BadConfigFile
}

impl fmt::Display for LoadError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            FileNotFound => write!(fmt, "(file not found)"),
            IncorrectPermissions => write!(fmt, "(incorrect permissions)"),
            BadConfigFile => write!(fmt, "(incorrect config file)")
        }
    }
}

impl error::Error for LoadError {
    fn description(&self) -> &str {
        match *self {
            FileNotFound => "file path error",
            IncorrectPermissions => "incorrect permissions error",
            BadConfigFile => "bad config error"
        }
    }
}

pub struct Heartbeat<F> {
    servers: Vec<SocketAddr>,
    result_handler: F,
    frequency: u32
}

impl<F> Heartbeat<F> where F: Fn(PulseResult) -> () {
    #[inline]
    pub fn new(servers: Vec<SocketAddr>, f: F, freq: u32) -> Heartbeat<F> {
        Heartbeat{servers: servers, result_handler: f, frequency: freq}
    }

    #[inline]
    pub fn load(s: &Path, f: F) -> LoadResult<F> {
        use std::io::Read;
        use std::str::FromStr;

        let mut file = match fs::File::open(&s) {
            Ok(f) => f,
            Err(_) => return Err(FileNotFound),
        };

        let mut file_str = String::new();
        let _ = file.read_to_string(&mut file_str);

        let val = match toml::Parser::new(&file_str[..]).parse() {
            Some(val) => val,
            None => return Err(BadConfigFile),
        };

        let config = match val.get("configuration") {
            Some(tbl) => tbl,
            None => return Err(BadConfigFile)
        };

        let config_val = match config.lookup("servers") {
            Some(arr) => arr,
            None => return Err(BadConfigFile)
        };

        let servers_val = match *config_val {
            toml::Value::Array(ref vec) => vec,
            _ => return Err(BadConfigFile)
        };

        let frequency_val = match config.lookup("frequency") {
            Some(val) => val,
            None => return Err(BadConfigFile)
        };

        let freq = match *frequency_val {
            toml::Value::Integer(val) => val as u32,
            _ => return Err(BadConfigFile)
        };

        let mut servers: Vec<SocketAddr> = Vec::with_capacity(servers_val.len());
        for server in servers_val.iter() {
            let addr_str = match *server {
                toml::Value::String(ref str) => str.clone(),
                _ => return Err(BadConfigFile)
            };

            let addr: SocketAddr = match SocketAddr::from_str(&addr_str[..]) {
                Ok(addr) => addr,
                Err(_) => return Err(BadConfigFile)
            };

            servers.push(addr);
        }

        Ok(Heartbeat{servers: servers, result_handler: f, frequency: freq})
    }

    #[inline]
    pub fn start(&self) {
        loop {
            sleep_ms(self.frequency);
            for server in self.servers.iter() {
                let r = TcpStream::connect(server);
                match r {
                    Ok(_) => (self.result_handler) (Ok(server)),
                    Err(_) => (self.result_handler) (Err(server))
                };
            }
        }
    }

    #[inline]
    pub fn add_server(&mut self, addr: SocketAddr) {
        self.servers.push(addr);
    }
}

#[test]
fn new_works() {
    use std::str::FromStr;
    let s1: SocketAddr = SocketAddr::from_str("127.0.0.1:8080").unwrap();
    let s2: SocketAddr = SocketAddr::from_str("127.0.0.1:6767").unwrap();
    let servers: Vec<SocketAddr> = vec![s1, s2];
    let client: Heartbeat<_> = Heartbeat::new(servers, |addr| println!("{:?}", addr), 1000);
}