#![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);
}