1#![doc(html_root_url = "https://docs.rs/senstate/0.0.1")]
2
3use std::sync::mpsc::{channel, Sender};
4use std::thread::JoinHandle;
5
6use log::{trace, warn};
7use num_traits::Num;
8use serde::Serialize;
9use serde_repr::Serialize_repr;
10use tungstenite::{connect, Message};
11use url::Url;
12use uuid::Uuid;
13
14#[derive(Serialize)]
15#[serde(rename_all = "camelCase")]
16struct AppMeta {
17 pub app_id: Uuid,
18 pub name: String,
19}
20
21#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize_repr)]
22#[repr(u8)]
23pub enum WatchType {
25 String,
28 Number,
31 Json,
34 Performance,
37}
38
39#[derive(Serialize)]
40#[serde(rename_all = "camelCase")]
41struct WatcherMeta {
42 pub watch_id: Uuid,
43 pub tag: String,
44 #[serde(rename = "type")]
45 pub type_: WatchType,
46}
47
48#[derive(Serialize)]
49#[serde(rename_all = "camelCase")]
50struct WatchData<T> {
51 pub watch_id: Uuid,
52 pub data: T,
53}
54
55#[derive(Serialize)]
56#[serde(rename_all = "camelCase")]
57struct LogData<T> {
58 pub log: T,
59}
60
61#[derive(Debug)]
64pub struct Client {
65 handle: JoinHandle<()>,
66 tx: Sender<String>,
67}
68
69#[derive(Serialize)]
70struct Payload<T> {
71 event: &'static str,
72 data: T,
73}
74
75impl Client {
76 pub fn new_watcher<T>(&self, tag: T, type_: WatchType) -> Watcher
80 where
81 T: Into<String>,
82 {
83 let id = Uuid::new_v4();
84 Self::add_watcher(
85 &self.tx,
86 WatcherMeta {
87 watch_id: id,
88 tag: tag.into(),
89 type_,
90 },
91 );
92
93 Watcher {
94 tx: self.tx.clone(),
95 id,
96 type_,
97 }
98 }
99
100 pub fn log<T>(&self, log: T)
102 where
103 T: Serialize,
104 {
105 input_log_event(&self.tx, LogData { log })
106 }
107
108 fn connect(url: Url) -> (JoinHandle<()>, Sender<String>) {
109 let (mut socket, _) = connect(url).expect("Can't connect");
110
111 let (tx, rx) = channel();
112 let handle = std::thread::spawn(move || loop {
113 let msg: String = rx.recv().unwrap();
114
115 trace!("--> {}", msg);
116 socket.write_message(Message::Text(msg)).unwrap();
117 });
118
119 (handle, tx)
120 }
121
122 fn add_app(tx: &Sender<String>, meta: AppMeta) {
123 send(tx, "addApp", &meta);
124 }
125
126 fn add_watcher(tx: &Sender<String>, meta: WatcherMeta) {
127 send(tx, "addWatcher", &meta);
128 }
129
130 pub fn wait(self) {
132 self.handle.join().unwrap();
133 }
134}
135
136#[derive(Debug, Clone)]
139pub struct Watcher {
140 tx: Sender<String>,
141 id: Uuid,
142 type_: WatchType,
143}
144
145impl Watcher {
146 pub fn send_string<T>(&self, data: T)
149 where
150 T: Into<String>,
151 {
152 if self.type_ != WatchType::String {
153 warn!("trying to send string with a {:?} watcher", self.type_);
154 }
155
156 input_event(
157 &self.tx,
158 WatchData {
159 watch_id: self.id,
160 data: data.into(),
161 },
162 )
163 }
164
165 pub fn send_number<T>(&self, data: T)
168 where
169 T: Num + Serialize,
170 {
171 if self.type_ != WatchType::Number {
172 warn!("trying to send number with a {:?} watcher", self.type_);
173 }
174
175 input_event(
176 &self.tx,
177 WatchData {
178 watch_id: self.id,
179 data,
180 },
181 )
182 }
183
184 pub fn send_json<T>(&self, data: T)
187 where
188 T: Serialize,
189 {
190 if self.type_ != WatchType::Json {
191 warn!("trying to send JSON with a {:?} watcher", self.type_);
192 }
193
194 input_event(
195 &self.tx,
196 WatchData {
197 watch_id: self.id,
198 data,
199 },
200 )
201 }
202
203 pub fn send_performance<T>(&self, data: T)
207 where
208 T: Num + Serialize,
209 {
210 if self.type_ != WatchType::Performance {
211 warn!("trying to send performance with a {:?} watcher", self.type_);
212 }
213
214 input_event(
215 &self.tx,
216 WatchData {
217 watch_id: self.id,
218 data,
219 },
220 )
221 }
222
223 pub fn log<T>(&self, log: T)
225 where
226 T: Serialize,
227 {
228 input_log_event(&self.tx, LogData { log })
229 }
230}
231
232pub fn new<T>(url: Url, name: T) -> Client
235where
236 T: Into<String>,
237{
238 let (handle, tx) = Client::connect(url);
239
240 Client::add_app(
241 &tx,
242 AppMeta {
243 app_id: Uuid::new_v4(),
244 name: name.into(),
245 },
246 );
247
248 Client { handle, tx }
249}
250
251fn send<T>(tx: &Sender<String>, event: &'static str, data: &T)
252where
253 T: Serialize,
254{
255 let data = Payload { event, data };
256 let data = serde_json::to_string(&data).unwrap();
257
258 tx.send(data).unwrap();
259}
260
261fn input_event<T>(tx: &Sender<String>, event: WatchData<T>)
262where
263 T: Serialize,
264{
265 send(tx, "inputEvent", &event);
266}
267
268fn input_log_event<T>(tx: &Sender<String>, event: LogData<T>)
269where
270 T: Serialize,
271{
272 send(tx, "inputLogEvent", &event);
273}