#![doc(html_root_url = "https://docs.rs/senstate/0.0.1")]
use std::sync::mpsc::{channel, Sender};
use std::thread::JoinHandle;
use log::{trace, warn};
use num_traits::Num;
use serde::Serialize;
use serde_repr::Serialize_repr;
use tungstenite::{connect, Message};
use url::Url;
use uuid::Uuid;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct AppMeta {
pub app_id: Uuid,
pub name: String,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize_repr)]
#[repr(u8)]
pub enum WatchType {
String,
Number,
Json,
Performance,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct WatcherMeta {
pub watch_id: Uuid,
pub tag: String,
#[serde(rename = "type")]
pub type_: WatchType,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct WatchData<T> {
pub watch_id: Uuid,
pub data: T,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct LogData<T> {
pub log: T,
}
#[derive(Debug)]
pub struct Client {
handle: JoinHandle<()>,
tx: Sender<String>,
}
#[derive(Serialize)]
struct Payload<T> {
event: &'static str,
data: T,
}
impl Client {
pub fn new_watcher<T>(&self, tag: T, type_: WatchType) -> Watcher
where
T: Into<String>,
{
let id = Uuid::new_v4();
Self::add_watcher(
&self.tx,
WatcherMeta {
watch_id: id,
tag: tag.into(),
type_,
},
);
Watcher {
tx: self.tx.clone(),
id,
type_,
}
}
pub fn log<T>(&self, log: T)
where
T: Serialize,
{
input_log_event(&self.tx, LogData { log })
}
fn connect(url: Url) -> (JoinHandle<()>, Sender<String>) {
let (mut socket, _) = connect(url).expect("Can't connect");
let (tx, rx) = channel();
let handle = std::thread::spawn(move || loop {
let msg: String = rx.recv().unwrap();
trace!("--> {}", msg);
socket.write_message(Message::Text(msg)).unwrap();
});
(handle, tx)
}
fn add_app(tx: &Sender<String>, meta: AppMeta) {
send(tx, "addApp", &meta);
}
fn add_watcher(tx: &Sender<String>, meta: WatcherMeta) {
send(tx, "addWatcher", &meta);
}
pub fn wait(self) {
self.handle.join().unwrap();
}
}
#[derive(Debug, Clone)]
pub struct Watcher {
tx: Sender<String>,
id: Uuid,
type_: WatchType,
}
impl Watcher {
pub fn send_string<T>(&self, data: T)
where
T: Into<String>,
{
if self.type_ != WatchType::String {
warn!("trying to send string with a {:?} watcher", self.type_);
}
input_event(
&self.tx,
WatchData {
watch_id: self.id,
data: data.into(),
},
)
}
pub fn send_number<T>(&self, data: T)
where
T: Num + Serialize,
{
if self.type_ != WatchType::Number {
warn!("trying to send number with a {:?} watcher", self.type_);
}
input_event(
&self.tx,
WatchData {
watch_id: self.id,
data,
},
)
}
pub fn send_json<T>(&self, data: T)
where
T: Serialize,
{
if self.type_ != WatchType::Json {
warn!("trying to send JSON with a {:?} watcher", self.type_);
}
input_event(
&self.tx,
WatchData {
watch_id: self.id,
data,
},
)
}
pub fn send_performance<T>(&self, data: T)
where
T: Num + Serialize,
{
if self.type_ != WatchType::Performance {
warn!("trying to send performance with a {:?} watcher", self.type_);
}
input_event(
&self.tx,
WatchData {
watch_id: self.id,
data,
},
)
}
pub fn log<T>(&self, log: T)
where
T: Serialize,
{
input_log_event(&self.tx, LogData { log })
}
}
pub fn new<T>(url: Url, name: T) -> Client
where
T: Into<String>,
{
let (handle, tx) = Client::connect(url);
Client::add_app(
&tx,
AppMeta {
app_id: Uuid::new_v4(),
name: name.into(),
},
);
Client { handle, tx }
}
fn send<T>(tx: &Sender<String>, event: &'static str, data: &T)
where
T: Serialize,
{
let data = Payload { event, data };
let data = serde_json::to_string(&data).unwrap();
tx.send(data).unwrap();
}
fn input_event<T>(tx: &Sender<String>, event: WatchData<T>)
where
T: Serialize,
{
send(tx, "inputEvent", &event);
}
fn input_log_event<T>(tx: &Sender<String>, event: LogData<T>)
where
T: Serialize,
{
send(tx, "inputLogEvent", &event);
}