extern crate redis;
extern crate ws;
extern crate geojson;
use std::cell::RefCell;
use std::borrow::Cow;
use std::io;
use self::geojson::{GeoJson, Feature, Geometry, Value};
use self::ws::{Sender, Message, Handshake, Result, connect, Handler, CloseCode};
type NazarResult<T> = redis::RedisResult<T>;
pub type NazarSender = ws::Sender;
pub enum Types {
String(&'static str),
Int(isize),
Float(f32)
}
pub struct Client<'a> {
url: &'static str,
cmd: String,
args: RefCell<Vec<Cow<'a, str>>>,
}
struct WSClient {
out: Sender,
action: fn(&Sender, String),
}
impl Handler for WSClient {
fn on_open(&mut self, _shake: Handshake) -> Result<()> {
self.out.send("Connected")
}
fn on_shutdown(&mut self) {
println!("Handler received WebSocket shutdown request.");
self.out.close(CloseCode::Away).unwrap();
}
fn on_close(&mut self, code: CloseCode, reason: &str) {
println!("Connection closing due to ({:?}) {}", code, reason);
self.out.close(code).unwrap();
}
fn on_message(&mut self, msg: Message) -> Result<()> {
match msg.into_text() {
Ok(m) => (self.action)(&self.out, m),
Err(e) => println!("[*] ERROR while reading message from server, {:?}", e),
}
Ok(())
}
}
impl<'a> Client<'a> {
pub fn new() -> Self {
Client { url: "", cmd: String::new(), args: RefCell::new(Vec::new()) }
}
pub fn from(url: &'static str) -> Self {
Client { url, cmd: String::new(), args: RefCell::new(Vec::new()) }
}
pub fn cmd(&mut self, s: &'a str) -> &Client<'a> {
self.cmd = String::from(s);
self
}
pub fn arg<A>(&self, a: A) -> &Client<'a>
where A: Into<Cow<'a, str>>
{
let v: Cow<'a, str> = a.into();
if !v.to_string().is_empty() {
self.args.borrow_mut().push(v);
} else {
println!("* [WARNING] arg cannot be empty. Skipping...");
}
self
}
pub fn ping(url: &'a str) -> bool
{
assert!(url != "");
let cmd = redis::cmd("PING");
let result: NazarResult<String> = cmd.query(&get_connection(url).unwrap());
match result {
Ok(r) => {
r == "PONG"
}
Err(_) => {
false
}
}
}
pub fn execute_with_args(&self) -> NazarResult<String> {
if !self.cmd.is_empty() {
let mut command = redis::cmd(&self.cmd[..]);
for a in self.args.borrow().iter() {
command.arg(a.to_string());
}
command.query(&get_connection(self.url)?)
} else {
println!(" [ERROR] Command cannot be empty!");
Err(redis::RedisError::from(io::Error::new(io::ErrorKind::NotFound, "Command cannot be empty")))
}
}
pub fn execute(self, command: &str, args: Vec<Types>) -> NazarResult<String> {
let mut command = redis::cmd(command);
for e in args {
match e {
Types::Int(arg) => command.arg(arg),
Types::String(arg) => command.arg(arg),
Types::Float(arg) => command.arg(arg),
};
}
command.query(&get_connection(self.url)?)
}
pub fn open_fence<F>(self, url: &str, fleet: &str, lat: &str, lng: &str, radius: &str, work: F) where F: Fn(String) {
let cmd_url = format!("{}/NEARBY+{}+FENCE+POINT+{}+{}+{}", url, fleet, lat, lng, radius);
ws::connect(cmd_url, |_out| {
|msg: ws::Message| {
match msg.into_text() {
Ok(m) => work(m),
Err(e) => println!("ERR: {:?}", e),
}
Ok(())
}
}).unwrap()
}
pub fn open_fence_within<F>(self, url: &str, fleet: &str, id: &str, coordinates: Vec<Vec<f64>>, work: F) where F: Fn(String) {
let geo_json = create_geo_json(id, coordinates.clone());
let cmd_url = format!("{}/WITHIN+{}+FENCE+OBJECT+{}", url, fleet, geo_json);
ws::connect(cmd_url, |_out| {
|msg: ws::Message| {
match msg.into_text() {
Ok(m) => work(m),
Err(e) => println!("ERR {:?}", e),
}
Ok(())
}
}).unwrap()
}
pub fn open_fence2(self, url: &str, fleet: &str, lat: &str, lng: &str, radius: &str, action: fn(&NazarSender, String)) {
let cmd_url = format!("ws://{}/NEARBY+{}+FENCE+POINT+{}+{}+{}", url, fleet, lat, lng, radius);
connect(cmd_url, |out: Sender| {
WSClient { out, action }
}).unwrap()
}
pub fn open_fence_within2(self, url: &str, fleet: &str, id: &str, coordinates: Vec<Vec<f64>>, action: fn(&NazarSender, String)) {
let geo_json = create_geo_json(id, coordinates.clone());
let cmd_url = format!("{}/WITHIN+{}+FENCE+OBJECT+{}", url, fleet, geo_json);
connect(cmd_url, |out: Sender| {
WSClient { out, action }
}).unwrap()
}
pub fn open_fence_and_send(self, url: &str, fleet: &str, lat: &str, lng: &str, radius: &str, client: &NazarSender) {
let cmd_url = format!("{}/NEARBY+{}+FENCE+POINT+{}+{}+{}", url, fleet, lat, lng, radius);
ws::connect(cmd_url, |_out| {
|msg: ws::Message| {
match msg.into_text() {
Ok(m) => client.send(m).unwrap(),
Err(e) => println!("ERR: {:?}", e),
}
Ok(())
}
}).unwrap()
}
pub fn open_fence_within_and_send(self, url: &str, fleet: &str, id: &str, coordinates: Vec<Vec<f64>>, client: &NazarSender) {
let geo_json = create_geo_json(id, coordinates.clone());
let cmd_url = format!("{}/WITHIN+{}+FENCE+OBJECT+{}", url, fleet, geo_json);
ws::connect(cmd_url, |_out| {
|msg: ws::Message| {
match msg.into_text() {
Ok(m) => client.send(m).unwrap(),
Err(e) => println!("ERR {:?}", e),
}
Ok(())
}
}).unwrap()
}
}
fn create_geo_json(id: &str, coordinates: Vec<Vec<f64>>) -> String {
let geometry = Geometry::new(
Value::Polygon(vec![coordinates])
);
let geojson = GeoJson::Feature(Feature {
id: Some(json!(id)),
properties: None,
bbox: None,
geometry: Some(geometry),
foreign_members: None,
});
geojson.to_string()
}
fn get_connection(url: &str) -> redis::RedisResult<redis::Client> {
redis::Client::open(url)
}